반응형

 다들 알다시피 많은 앱을 사용하다보면 리스트 형태의 구조를 아주 많이 볼 수 있다.  

'리스트뷰(ListView)'를 사용할 수도 있지만, 보다 효율적이어서 더 많이 사용하는게 '리사이클러뷰(RecyclerView)' 이다. 

(리사이클러뷰가 어떤 것인지는 https://habilism.tistory.com/7 이 글을 참고하면 좋을 듯 하다. )

 

 그런데 리사이클러뷰를 사용할 때에는 코드에 미리 아이템의 데이터를 입력해둘 때도 있지만,

사용자가 원할 때 원하는 아이템을 추가해줘야 할 때도 많다.  

 

 

 

 이번 시간에는 메인 액티비티에 있는 버튼을 누르면 다른 액티비티인 작성 액티비티로 화면을 전환하여

데이터 입력을 하고, 작성 액티비티에 있는 버튼을 누르면 다시 메인 액티비티로 화면이 전환되면서

입력했던 데이터들이 어댑터를 통해 메인 액티비티의 리사이클러뷰에 추가되는 방법에 대해서 알아볼 것이다.  

 

 먼저, 메인 액티비티에서 버튼을 누르면 화면 전환이 되는데, 전환이 되고 끝나는 게 아니라, 데이터들을 받아서

다시 메인 액티비티 화면으로 돌아올 것이므로 인텐트를 보낼 때 startActivity(intent)가 아닌 startActivityForResult(intent, requestCode)를 호출할 것이다. 

 

1
2
  Intent intent = new Intent(getApplicationContext(), WriteActivity.class);
        startActivityForResult(intent, 101);
cs

 여기서 requestCode. 즉, 요청 코드가 무엇이냐면 지금 우리가 구현할 기능은 두 개의 액티비티끼리만 화면 전환을 하여 데이터를 주고 받지만, 더 큰 규모의 앱을 만들게 되면 많은 양의 화면이 왔다갔다하면서 데이터를 주고 받게 되는데, 이럴 때에는 requestCode를 사용해서 어디서 온 액티비티인지 구분을 할 수 있다. 

 

 다시 데이터를 돌려 받을 때는 콜백 메소드인 onActivityResult()를 통해서 받는데, 이 메소드는 조금 뒤에 알아보도록

하자. 

 

 이렇게 화면을 전환하면 데이터를 입력할 수 있는 액티비티인 WriteActivity 화면으로 오게 된다. 

여기서 사용자가 원하는 데이터를 입력하면 된다.

데이터 입력 후, 버튼을 눌러서 입력한 데이터들을 다시 메인 액티비티로 보내야하는데 아래의 코드처럼

작성하면 보낼 수 있다. 

 

1
2
3
4
5
6
7
8
public void sendMemo() {
        String memo = memoEdt.getText().toString(); 
 
        Intent intent = new Intent(this, MainActivity.class);
        intent.putExtra("memo", memo);
        setResult(RESULT_OK, intent);
        finish();
    }
cs

 memoEdt라는 EditText에 원하는 문자열을 입력하고 입력한 문자열을 문자열 변수 memo에 할당한다.

그리고 메인 액티비티로 보내는 인텐트를 생성하고 그 인텐트에 memo를 넣어서

인텐트를 finish() 통해 이전 액티비티인 메인 액티비티로 보낸다. 

 

 이렇게 보낸 인텐트를 받아야 하는데, 메인 액티비티의 onActivityResult() 콜백 메소드로 받아서 처리할 수 있다. 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
 
        if (requestCode == 101) { 
            if (intent != null) {
                String memo = intent.getStringExtra("memo");
 
                adapter.addItem(new Memo(memo)); 
                adapter.notifyDataSetChanged();
            }
        }
    }
cs

 우리가 아까 메인 액티비티에서 작성 액티비티로 인텐트를 보낼 때 requestCode에 101를 지정했었다.

그래서 다시 돌려 받을 때 requestCode가 101인 것만 받아서 처리할 수 있다. 

 

 위처럼 받은 인텐트에서 "memo"라는 것을 뽑아내어 문자열 변수 memo에 할당한다.

그리고 memo를 어댑터의 addItem() 메소드로 새 아이템을 추가하면된다. 

(notifyDataSetChanged()를 해야 갱신이 되기 때문에 잊지말고 해주자.)

 

이렇게 리사이클러뷰에 새 아이템을 추가하는 방법에 대해서 알아보았다.

 

피드백은 언제나 환영입니다. 

 

 

반응형
반응형

 액티비티에서 다른 액티비티로 데이터를 전달해야하는 경우가 상당히 자주 있다. 

이렇게 데이터를 다른 액티비티로 전달할 때에는 '인텐트(Intent)'에 '부가 데이터(Extra Data)'를 넣어서 전달할 수 있는데

 

어떻게 데이터를 주고 받을까? 

 인텐트 안에는 '번들(Bundle)' 객체가 들어 있는데, 이것은 해시테이블과 비슷해서 putExtra()로 데이터를 넣고

getOOOExtra()로 데이터를 가져오는 방식으로 데이터를 주고 받을 수 있다. 

 

 데이터를 전달하는 방식 중에 아예 객체 자체를 전달하는 방법도 있는데, 그것은 바로 'Parcelable' 인터페이스를 이용하는 것이다. Parcelable 인터페이스는 Serializable 인터페이스와 비슷하지만 Serialization 했을 때 크기가 작아서

안드로이드 내부의 데이터 전달에 자주 사용된다. 이렇게 Parcelable 인터페이스를 사용하면 객체 자체를 번들에

넣어서 데이터를 전달할 수 있게된다.

 

Parcelable를 사용하려면 두 개의 메소드를 상속해야하는데,

첫 번째는 describeContents() 메소드이다. 이것은 Serialization 하려는 객체의 유형을 구분할 때 사용된다. 

두 번째는 writeToParcel() 메소드이다. 이것은 객체가 가지고 있는 데이터를 Parcel 객체로 만들어준다. 

 그리고 Parcel 객체는 번들 객체의 putExtra()나 getOOOExtra() 메소드처럼 readOOO(), writeOOO() 메소드로 데이터를 읽고 쓸 수 있다. 

 

 Parcelable를 이해하기 위한 예제를 해보겠다. 제목과 내용을 입력한 다음에 버튼을 누르면 액티비티 화면이 전환이

되면서 입력했던 데이터들이 전달되어 TextView에 출력하는 간단한 앱이다. 

 

 먼저, Parcelable 인터페이스부터 만들어보자. 

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
31
32
33
34
35
36
37
38
39
40
41
42
43
public class ExampleData implements Parcelable { // Parcelable 인터페이스를 상속
 
    String title;
    String contents;
 
    public ExampleData(String tit, String content) {
        title = tit;
        contents = content;
    }
 
    public ExampleData(Parcel src) { // Parcel 객체에서 읽기
        // 데이터를 읽어서 변수에 할당
        title = src.readString();
        contents = src.readString();
    }
 
    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { // Parcel 객체로부터 데이터를 읽어서 객체 생성
 
        @Override
        public ExampleData createFromParcel(Parcel src) { 
            return new ExampleData(src); // ExampleData 생성자 호출 후 Parcel 객체에서 읽기
        }
 
        // 여기에서 Parcel은 객체 안에 있는 데이터를 다른 곳에 전달할 때 사용된다.
 
        @Override
        public ExampleData[] newArray(int size) {
            return new ExampleData[size];
        }
    };
 
    @Override
    public int describeContents() {
        return 0;
    }
 
    @Override
    public void writeToParcel(Parcel dest, int flags) { // Parcel 객체로 쓰기(객체가 가지고 있는 데이터를 Parcel 객체로 만듬)
        dest.writeString(title);
        dest.writeString(contents);
    }
// 즉, Parcel 객체의 데이터를 읽는 부분과 쓰는 부분을 정의하는 것임
 
cs

Parcelable 인터페이스를 만들었으면 데이터를 서로 주고 받을 MainActivity와 ReceiveActivity를 만들어보자.

 

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
31
32
public class MainActivity extends AppCompatActivity {
 
    public static final int REQUEST_CODE = 101;
    public static final String EXAMPLE_DAT_KEY = "data"// 번들 객체에 데이터를 저장하기 위한 Key
 
    EditText titleEdt, contentEdt;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        titleEdt = (EditText)findViewById(R.id.titleEdt);
        contentEdt = (EditText)findViewById(R.id.contentEdt);
 
        Button sendBtn = (Button)findViewById(R.id.sendBtn);
        sendBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 입력한 제목과 내용의 문자열을 변수에 할당
                String title = titleEdt.getText().toString();
                String contents = contentEdt.getText().toString();
 
                // 인텐트에 데이터를 넣어서 다른 액티비티로 전달함
                Intent intent = new Intent(getApplicationContext(), ReceiveActivity.class);
                ExampleData exampleData = new ExampleData(title, contents);
                intent.putExtra(EXAMPLE_DAT_KEY, exampleData);
                startActivityForResult(intent, REQUEST_CODE);
            }
        });
    }
}
cs
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
31
32
33
34
35
36
public class ReceiveActivity extends AppCompatActivity {
 
    public static final String EXAMPLE_DATA_KEY = "data"// 번들 객체에 데이터를 저장하기 위한 Key
 
    TextView tv;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_receive);
 
        tv = (TextView)findViewById(R.id.tv);
 
        Button backBtn = (Button)findViewById(R.id.backBtn);
        backBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
 
        Intent intent = getIntent(); // 인텐트 객체 반환, 객체 안의 번들 객체 참조
        processIntent(intent); // 인텐트를 전달해서 processIntent() 메소드에서 처리
    }
 
    private void processIntent(Intent intent) { // 번들 객체에서 꺼낸 후 TextView로 보여줌
        if(intent != null) {
            Bundle bundle = intent.getExtras();
            ExampleData exampleData = bundle.getParcelable(EXAMPLE_DATA_KEY);
 
            if(intent != null) {
                tv.setText("제목 : " + exampleData.title + "\n" +"내용 : " + exampleData.contents);
            }
        }
    }
}
cs

초기 실행 화면
데이터 입력 후 전송 버튼 누르기

 

최종 화면

 

피드백은 언제나 환영입니다. 

반응형
반응형

 많은 앱들을 보면 앱을 실행한 뒤 앱의 메인 화면이 화면에 보이기 전에 앱의 로고나 이름 등이 들어간

화면이 1초 정도 보이고 사라진다. 이러한 화면을 '스플래시 화면(Splash Screen') 이라고 한다. 

우리가 자주 사용하는 앱인 카카오톡이나 유튜브만 봐도 별거 아닌 기능이지만 앱의 완성도를 조금이나마 높혀준다.

 

 스플래시 화면은 이름 그대로 '화면'이기 때문에 액티비티로 만들 수 있다. 

우선 스플래시 화면을 위한 액티비티를 만들어보자. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class SplashActivity extends AppCompatActivity {
 
    Handler handler = new Handler();
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 우리가 보통 사용하는 액티비티와는 다르게 이 부분에 xml 파일의 인플레이션을 하는 setContentView 메소드가  없음
 
        handler.postDelayed(new Runnable() { // 핸들러로 지연시키는 메소드
            @Override
            public void run() {
                Intent intent = new Intent(getApplicationContext(), MainActivity.class);
                startActivity(intent);
 
                finish(); // 메인 화면에서 뒤로 가기를 눌렀을 때 앱이 꺼지게함, finish()가 없는 상태에서
                // 뒤로 가기를 누르면 다시 스플래시 화면으로 돌아가서 고정됨
            }
        }, 1000); // 핸들러로 1초 지연시킴
    }
}
cs

 

스플래시 화면을 xml파일이 아닌 '테마(Theme)' 를 이용해서 만들 것이니 xml은 그냥 만들어 놓기만 하면 된다. 

그리고 매니페스트 파일로 가서 첫 시작 화면을 다음과 같이 스플래시 화면으로 수정해야된다.

 

1
2
3
4
5
6
7
8
9
10
  <activity android:name=".MainActivity" />
        <activity
            android:name=".SplashActivity"
            android:theme="@style/SplashTheme">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
cs

그런데 위의 매니페스트 코드에서 좀 낯선 부분이 있을 것이다. 

이제 부터 우리가 다룰 '테마(Theme)' 라는 것인데 테마를 이용하면 앱 디자인의 세부사항을 UI 구조 및 동작과 

분리할 수 있다는 장점이 있다. 그 '테마'에 대한 정의를 스플래시 액티비티에 해놓았고 이제부터 테마를

직접 만들어보자.

 

매니페스트에 미리 정의해놓은 테마 이름을 res/values/theme.xml 파일에 들어가서 

1
2
3
 <style name="SplashTheme" parent="Theme.AppCompat.NoActionBar" >
        <item name="android:windowBackground" >@drawable/splash_background</item>
    </style>
cs

위와 같이 스타일로 설정해놓는다. 그리고 NoActionBar를 상속해서 액션바 부분이 안 보이도록 설정하고,

windowBackground 속성에 우리가 만들어야 할 드로어블을 지정해서 배경으로 보이게 할 것이다. 

 

그럼 드로어블 파일도 만들어보자.

 

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
 
    <item android:drawable="@drawable/splash_base" />
 
    <item android:top="210dp" >
        <bitmap android:src="@drawable/colorlogo" android:gravity="top" />
    </item>
</layer-list>
cs

위의 코드에서 보면 item 태그가 두 개가 들어 있는 것을 알 수 있는데,

첫 번째 item 태그는 화면의 전체를 채울 것이고, 두 번째 item 태그는 이미지를 가져와서 특정한 위치에

놓을 것이다. 여기서 또, 화면의 전체를 채울 드로어블 파일을 만들어야한다. 

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8"?>
 
    <gradient
        android:startColor="#acffef"
        android:centerColor="#cbff75"
        android:endColor="#66f8f0"
        android:angle="180"
        android:centerY="1.5" />
 
    <corners android:radius="0dp" />
</shape>
cs

배경색을 위와 같이 설정할 수 있다. 본인이 원하는 대로 수정해보면 된다. 

 

 

 

피드백은 환영입니다. 

반응형
반응형

 앱을 사용하다보면 앱이 갑자기 중지되거나 다시 화면에 나타날 때가 있는데, 이럴 때에 앱의 현재 상태 정보가 저장

되고 복원되는 것은 상당히 중요하다. 예를 들어, 게임을 할 때 개인 점수 기록을 세웠다든지, 그 외 다른 앱을 사용할 때중요한 정보를 입력해 놓았을 때라든지 데이터를 저장해 놓지 않으면 앱을 다시 실행할 때 처음부터 다시해야하는 번거로움이 있게 된다. 

 

 보통 대량의 데이터를 저장할 때에는 데이터베이스를 사용하지만, 앱 안에서 간단한 데이터를 저장하거나 복원할 

때에는 'SharedPreferences' 를 사용할 수 있는데, 이 'SharedPreferences'는 앱 내부에 파일을 하나 만들고 이 파일

안에서 데이터를 저장하고 읽어올 수 있다. 

 

 그렇다면 이 'SharedPreferences'는 어떻게 사용을 할까? 

이번 시간에는 예제로 앱이 종료될 때(onDestroy()) 데이터를 저장하고, 앱을 다시 실행시킬 때(onCreate()) 

저장된 데이터를 불러오는 간단한 코드를 작성해볼 것이다. 

 

 먼저 화면에 TextView를 하나만 놓고 액티비티 코드를 보자.

 

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
public class MainActivity extends AppCompatActivity {
 
    TextView textView;
 
    String recordFile = "file";
 
    @Override
    protected void onCreate(Bundle savedInstanceState) { // 앱이 실행되면서 저장된 데이터를 불러옴
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        textView = (TextView) findViewById(R.id.textView);
 
        SharedPreferences sharedPreferences = getSharedPreferences(recordFile, Activity.MODE_PRIVATE);
        String record = sharedPreferences.getString("record""");
        textView.setText(record);
 
    }
 
    @Override
    protected void onDestroy() { // 앱이 중지가 되면서 데이터가 저장이됨
        super.onDestroy();
 
        SharedPreferences sharedPreferences = getSharedPreferences(recordFile, Activity.MODE_PRIVATE); // 데이터 저장소에 데이터를 저장하는 객체
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putString("record", textView.getText().toString()); // 저장하려는 데이터 설정
        editor.commit(); // 실제로 저장
    }
}
cs

 

위와 같이 약간의 SharedPreferences 코드 추가로 앱을 껐다가 다시 실행시켜도 데이터들이 사라지지 않고 그대로 사용할 수 있게 된다. 

 

피드백은 언제나 환영입니다. 

 

 

 

 

 

반응형
반응형

리사이클러뷰를 사용할 때 거꾸로 출력해야할 때가 있는데

굉장히 간단하게 구현할 수 있다.

 

1
2
3
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
layoutManager.setReverseLayout(true);
layoutManager.setStackFromEnd(true);
cs

위와 같이 레이아웃 매니저를 생성하고 

두 줄의 코드를 추가로 입력해주면 된다. 

 

 

 

피드백은 언제나 환영입니다. 

반응형
반응형

 안드로이드 앱을 사용하면서 여러 개의 선택지 중에서 원하는 하나를 고르거나 또는 여러 개를 골라야할 때가 있다. 

이럴 때 사용하는 것이 '메뉴(Menu)' 인데, 메뉴는 평소에 아주 많이 쓰이는 UI 요소이고, 모든 기능들을 계층적으로 

표시할 수 있다.  이렇게 안드로이드 메뉴에는 옵션 메뉴, 컨텍스트 메뉴, 팝업 메뉴와 같은 여러 가지의 종류의 메뉴가 있는데, 이번 시간에는 액티비티의 주된 메뉴라고 할 수 있는 '옵션 메뉴(Option Menu)' 를 알아볼 것이다. 

 

 먼저, '옵션 메뉴(Option Menu)' 를 만드려면 xml로 메뉴를 정의해야한다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
 
    <item
        android:id="@+id/two"
        android:title="2x2x2"
        app:showAsAction="never" />
    <item
        android:id="@+id/three"
        android:title="3x3x3"
        app:showAsAction="never" />
    <item
        android:id="@+id/onehand"
        android:title="3x3x3 One-Handed"
        app:showAsAction="never" />
</menu>
cs

위의 xml 코드에서 보면, app:showAsAction이라는 속성이 보일 것이다. 

이 속성은 메뉴 아이템들이 어떻게 보일 것인가에 대해 설정하는 것으로써 다음과 같은 속성들이 있다. 

showAsAction 속성 값 비고
ifRoom 앱 바에 표시할 여유 공간이 있을 때만 아이템을 표시한다.
always 항상 앱 바에 메뉴 아이템을 추가하여 표시한다.
never 앱 바에 아이템을 추가하여 표시하지 않는다. (디폴트, 더보기를 누르면 리스트 형식으로
표시됨)
withText android:title 속성에 정의한 제목을 표시한다.
collapseActionView 아이템에 설정한 뷰의 아이콘만 표시한다. 

메뉴를 위한 xml파일

 

xml 파일을 만들고 나면 이것을 액티비티에다가 '팽창(Inflate)' 시켜줘야한다. 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.event_list, menu);
        return true;
    }
 
    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.two:
                Toast.makeText(getApplicationContext(), "2x2x2 종목이 선택되었습니다. ", Toast.LENGTH_SHORT).show();
                break;
 
            case R.id.three:
                Toast.makeText(getApplicationContext(), "3x3x3 종목이 선택되었습니다. ", Toast.LENGTH_SHORT).show();
                break;
 
            case R.id.onehand:
                Toast.makeText(getApplicationContext(), "3x3x3한손 종목이 선택되었습니다. ", Toast.LENGTH_SHORT).show();
                break;
        }
        return super.onOptionsItemSelected(item);
    }
cs

 

이렇게 간단한 코드를 작성해주면 메뉴 xml이 액티비티에 팽창이 되어서

앱 바의 더보기 아이콘을 누르면 옵션 메뉴들이 리스트 형식으로 나타나게 된다. 

 

 

피드백은 언제나 환영입니다. 

반응형
반응형

안드로이드 앱의 4대 구성 요소를 컴포넌트라고 부르는데, '컴포넌트(Component)' 중 하나인

'서비스(Service)'는 이전 시간에 알아보았고, 이번에는 또 다른 4대 컴포넌트인

'방송수신자/브로드 캐스트 리시버(Brodcast Receiver)'를 알아보겠다.

 

안드로이드에서 '방송(Brodcasting)' 이란 메시지를 여러 객체에 전달하는 것을 말한다.

즉, 이런 방송을 받고 싶다면 '방송 수신자(Brodcast Receiver)' 를 만들어서 앱에 등록해주면 된다. 

 

 

 

앱을 사용하다보면 사건이 발생하거나 상태가 변경되는 등 많은 이벤트들이 발생하는데, 

방송 수신자는 이것들을 시스템에게 통지받기를 원한다.

 

예를 들어, 다른 사람으로부터 문자 메시지를 받았을 때 이 문자 메시지를 SMS 앱에 알려줘야 한다면

브로드 캐스팅 방식으로 전달하면 된다. 이렇게 전달하게 되면 단말 전체에 적용이 되기 때문에

이런 방식을 '글로벌 이벤트(Global Event)' 라고 부른다.

 

즉, '문자 메시지가 도착했습니다.", "전화가 왔습니다." 와 같은 것이다.

 

위에서 언급했다시피, 방송 수신자도 인텐트나 서비스와 마찬가지로 앱의 구성 요소이기 때문에

매니페스트에 반드시 등록을 해줘서 시스템이 알게 해줘야한다.

그리고 원하는 메시지만 받으려면 인텐트 필터를 사용해서 시스템에 등록해주면 된다.

(모든 메시지는 인텐트 안에 넣어서 전달되므로)

 

이제 직접 방송 수신자를 만들고 SMS 문자 메시지를 받아서 발신자 번호, 메시지 내용, 수신 시간을

출력하는 앱을 만들어 보겠다.

 

우선 매니페스트에 등록부터 하도록 하겠다.

 

1
2
3
4
5
6
7
8
 <receiver android:name=".SmsReceiver"
            android:enabled="true"
            android:exported="true" >
 
            <intent-filter>
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver>
cs

그리고 SMS를 수신하려면 권한도 필요하다.

 

<uses-permission android:name="android.permission.RECEIVE_SMS" />

 

그 다음으로는 BrodcastReceiver 클래스를 상속 받는 새로운 클래스를 생성한다.

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class SMSReceiver extends BroadcastReceiver {
 
    private static final String TAG = "SMSReceiver"// logt 를 입력하면 자동으로 태그 생성
 
    public SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 한국식 날짜 표기 형식으로 변경
 
    @Override // 메시지를 받으면 onReceive()가 자동 호출됨
    public void onReceive(Context context, Intent intent) { // 인텐트 안에는 문자 메시지 데이터가 들어가있음 -> 시스템이 Telephony 모듈에서 넣어줌
        Log.i(TAG, "문자 메시지를 받았습니다. " );
 
        Bundle bundle = intent.getExtras(); // 인텐트에서 번들 객체 가져오기
        SmsMessage[] smsMessage = mySmsMessage(bundle); // mySmsMessage() 메소드로 SMS 메시지 객체 생성
 
        if(smsMessage != null && smsMessage.length > 0) { // 메시지가 널 값이 아니고, 메시지의 길이가 0보다 크면
 
            String sender = smsMessage[0].getOriginatingAddress(); // 발신자 번호 가져오기
            Log.i(TAG, "발신자 번호 : " + sender);
 
            String content = smsMessage[0].getMessageBody(); // 문자 내용 가져오기
            Log.i(TAG, "문자 내용 : " + content);
 
            Date date = new Date(smsMessage[0].getTimestampMillis()); // 수신 시간 가져오기
            Log.i(TAG, "수신 시간 : " + date.toString());
            sendToMain(context, sender, content, date); // 메인 액티비티로 인텐트를 보내기 위한 메소드
        }
    }
 
    private SmsMessage[] mySmsMessage(Bundle bundle) { // SmsMessage 라는 자료형으로 된 배열 객체 리턴
        Object[] objects = (Object[]) bundle.get("pdus"); // Bundle 객체에 들어가 있는 부가 데이터 중에서 pdus 가져오기 (pdus에는 SMS데이터와 관련된 내용들이 들어가있음)
        SmsMessage[] smsMessages = new SmsMessage[objects.length];
 
        int smsCount = objects.length;
 
        for(int i = 0; i < smsCount; i++) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // 개인 단말의 안드로이드 버전이 버전 6보다 크면
                String format = bundle.getString("format");
                // 인텐트 객체 안에 부가 데이터로 들어가있는 SMS 데이터를 확인하려면 SmsMessage 클래스의 createFromPdu 메소드로 Smsmessage 객체로 리턴하면 SMS 데이터를 확인할 수 있음
                smsMessages[i] = SmsMessage.createFromPdu((byte[]) objects[i], format);
            } else { // 6이전 버전이면
                smsMessages[i] = SmsMessage.createFromPdu((byte[]) objects[i]);
            }
        }
        return smsMessages; // Bundle 객체의 인텐트 안에 부가 데이터로 들어가있던 데이터를 이용하여 SmsMessage 객체로 변환해서 던져줌
   }
 
   private void sendToMain(Context context, String sender, String content, Date date) {
        Intent intent = new Intent(context, MainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
        
        intent.putExtra("sender", sender);
        intent.putExtra("content", content);
        intent.putExtra("date", dateformat.format(date));
        
        context.startActivity(intent);
   }
}
 
cs
 

조금 복잡해보일 수 있으나 mySmsMessage() 메소드는 SMS 데이터를 확인할 수 있도록 안드로이드 API에 정해둔 

코드를 사용하므로 수정될 일이 거의 없기 때문에 다른 앱을 만들 때에도 재사용을 할 수가 있다.

 

이렇게 코드를 작성하고 단말로 문자 메시지를 보내게 되면

문자 메시지를 받은 후 로그창

로그창에 다음과 같이 받은 내용들이 출력된다.

 

이것을 로그창이 아닌 액티비티 화면에 출력하고 싶으면 인텐트를 이용해서 데이터를 전달하는 방식으로 하면 된다.

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
31
32
33
34
35
36
37
38
39
public class MainActivity extends AppCompatActivity {
 
    TextView senderTv, contentTv, dateTv;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        senderTv = (TextView)findViewById(R.id.senderTv);
        contentTv = (TextView)findViewById(R.id.contentTv);
        dateTv = (TextView)findViewById(R.id.dateTv);
 
        Intent passedIntent = getIntent(); // 전달받은 인텐트를 처리하도록
        processIntent(passedIntent); // 인텐트 처리 메소드 호출
    }
 
    @Override
    protected void onNewIntent(Intent intent) { // 액티비티가 이미 만들어져 있는 상태에서 전달 받은 인텐트를 처리하도록 설정
        processIntent(intent);
 
        super.onNewIntent(intent);
    }
 
    private void processIntent(Intent intent) { // 인텐트 처리 메소드, 인텐트 객체 안에 들어 있는 데이터를 꺼내서 TextView 에 설정
        if (intent != null) { // 인텐트 널 체크, 인텐트가 널이 아니면
 
            // 전달 받은 인텐트에서 발신자 번호, 메시지 내용, 수신 시간 값들을 가져오기
            String sender = intent.getStringExtra("sender");
            String content = intent.getStringExtra("content");
            String date = intent.getStringExtra("date");
 
            // 가져온 값들을 TextView에 출력
            senderTv.setText("발신자 번호 : " + sender);
            contentTv.setText(content);
            dateTv.setText("수신 시간 : " + date);
        }
    }
}
cs

초기 화면

 

로그창 때와 마찬가지로 단말로 문자 메시지를 보내게 되면 관련 내용이 로그창과 함께 액티비티 화면에 출력된다.

문자 받은 후 최종 화면

 

피드백은 언제나 환영입니다. 

반응형
반응형

(210522 수정)

 

 우리가 안드로이드 앱을 개발하다보면 반드시 포함되어있는 파일이 있는데,

그것은 '매니페스트 파일(Manifest File)' 이다. 

 매니페스트 파일은 모든 앱의 루트(가장 상위 폴더)에 위치해있어야 하며, 어떠한 컴포넌트(앱의 구성요소)가 있는지, 

어떠한 권한이 부여가 되어있는지, 앱의 패키지 이름은 무엇인지 등등 다양한 정보를 담고 있다. 

 

 다양한 정보를 담고 있는 매니페스트 파일에서 여러가지가 있지만 특히나 중요한 정보는 다음과 같다. 

 

1. 앱의 패키지 이름 지정 : 

매니페스트 파일의 '루트(Root)' 에는 다음과 같이 앱의 패키지 이름이 있어야한다.

1
2
3
<?xml version="1.0" encoding="utf-8"?>
    package="com.example.mvptest">
cs

그리고 최종적으로 APK로 추출하려고 빌드하는동안 '안드로이드 빌드 도구(Android Build Tool)' 의 패키지 이름

사용 목적은 두 가지가 있는데,

 

1. 첫 번째는 앱에서 만들어진 R.java 클래스의 NameSpace로 이 패키지 이름이 사용된다. 위 코드로 예를 들어,

"com.example.mvptest.R"가 생성된다.

2. 두 번째로 패키지 이름을 사용하여 매니페스트 파일에 등록되어있는 상대 경로의 클래스 이름에 사용된다.

예를 들어, 다음과 같이 매니페스트 파일에 등록된 액티비티가

1
 <activity android:name=".MainActivity">
cs

"com.example.mvptest.MainActivity" 이렇게 말이다.

이 처럼 매니페스트 파일의 패키지 이름은 액티비티와 앱의 코드가 있는 프로젝트의 기본 패키지 이름과 반드시

일치해야한다. 만약에 프로젝트 내에 다른 서브 패키지가 있더라도 매니페스트 내에 있는 패키지 속성에 따라 R.java

클래스가 생성된다.

 

 

2. 컴포넌트(앱의 구성 요소)에 대한 정보 등록 : 

앱의 4대 컴포넌트(앱의 구성 요소)에는 '액티비티(Activity)', '서비스(Service)', '방송 수신자(Brodcase Receiver)', '내용 제공자(Content Provider)' 가 있는데 이것들을 사용하기 위해서는 매니페스트 파일에 태그로 선언을 해서 앱에 등록해주어야한다. 

 

4대 컴포넌트에 대한 XML 태그는 다음과 같다.

<activity> : 액티비티(Activity)

<service> : 서비스(Service)

<receiver> : 방송 수신자(Brodcast Receiver)

<provider> : 내용 제공자(Cotent Provider)

 

만약에 등록을 하지 않고 앱을 실행시키게 되면 시스템에서 이를 시작시키지 못하여 앱을 죽고 만다. 

 

 

3. 앱이 가져야 하는 권한 정보 등록 : 

앱을 사용하는데에 있어서 연락처나 문자같은 보안에 민감한 사용자 데이터 또는 카메라나 인터넷 등과 같은 특정한

시스템 기능들에 액세스하기 위해서는 사용자 권한을 등록해줘야 하는데,

1
<uses-permission android:name="android.permission.INTERNET" />
cs

위와 같이  <uses-permission> 태그로 각각의 권한들을 고유한 레이블로 구성되어 식별된다.

이렇게 권한을 등록하게되면 앱이 보호된 기능을 사용할 수 있게 된다.

 

 

4. 앱에 필요한 하드웨어 및 소프트웨어 기능 / 호환성 : 

앱에 필요한 하드웨어나 소프트웨어의 기능. 즉, 앱과 호환될 수 있도록 유형을 등록할 수 있다. 구글 플레이 스토어에서는 앱에 반드시 필요한 기능이나 시스템 버전을 제공하지 않는 단말에게 앱의 설치를 허용하지 않기 때문에 이 점에

유의하면서 매니페스트 파일에 필요한 것들을 등록해줘야한다. 

1
2
 <uses-feature android:name="android.hardware.Camera"
        android:required="true" />
cs

위와 같이 <uses-feature> 태그를 사용하여 등록할 수 있다.

 

피드백은 언제나 환영입니다. 

 

반응형

+ Recent posts