">

Firebase를 통한 Admob 사용


1. Firebase에서 프로젝트를 추가한다. 

2. AdMob 페이지에 가서 등록하고 광고를 추가한다. 

에드몹 페이지 가기 >

3. AdMob 테스트 광고를 삽입해서 광고가 잘 나오는지 확인한다.  


Firebase 페이지에서 로그인 후 콘솔 화면으로 갑니다. 그리고 프로젝트를 하나 추가해 줍니다. 

프로젝트 추가 이후 좌측 하단에 AdMob 메뉴를 클릭합니다. 

AdMob은 Firebase에서 설명이 잘 되어 있어서 추가 설명은 하지 않겠습니다. 


단, 광고를 삽입하고 나서 광고 화면이 바로 나오지 않을텐데 에러가 없다면 잘 입력한 것이니 당황하지 않으셔도 됩니다. 

광고가 나오는 것을 확인하고 싶으면 아래 코드를 액티비티나 프래그먼트에 삽입해보세요.

아래 코드를 삽입하고 광고가 잘 나오면 시간이 좀 지나면 테스트 광고가 아닌 정상적인 광고가 잘 나옵니다. 

길게는 몇일이 걸릴 수도 있습니다. 



  AdView mAdview = (AdView) findViewById(R.id.adView);

  AdRequest adRequest = new AdRequest.Builder().addTestDevice(AdRequest.DEVICE_ID_EMULATOR).build();

  mAdview.loadAd(adRequest); 



'프로그래밍 > Android' 카테고리의 다른 글

커스텀 리스트뷰 만들기 - Chapter01  (0) 2017.03.18
해시키(Hash key) 가져오기  (0) 2017.03.15
FCM 구현 - 안드로이드  (0) 2017.02.28
당겨서 새로고침(Pull to refresh)  (0) 2017.02.20
Fragment 새로고침  (0) 2017.02.20

FCM 알아보기



GCM을 대신해 새로운 버전인 FCM이 나왔습니다. 안드로이드 앱에서 설정해줘야 하는 부분이 대폭 줄어서 개발자 입장에서는 편리한 점이 많은 것 같아요. 서버가 개발 되지 않았더라도 푸시 메시지를 잘 받을 수 있는지 확인도 가능합니다. 

Firebase는 엔드포인트단에서 쉽게 여러 가지 서비스가 가능하도록 도와줍니다. 

Firebase에 문서가 아직 좀 덜 정리된 느낌이여서 블로그에 정리해서 올려보았습니다. 순서대로만 하시면 Firebase에 설명보다 더 빠르게 FCM을 구성할 수 있을 겁니다. 


구현하기

1. 안드로이드 스튜디오를 통해 프로젝트를 하나 생성 합니다. 

2.  앱 수준의 build.gradle 파일에 FCM 종속 항목을 추가합니다. 


3. 모듈의 Gradle 파일에 플러그인 사용 설정을 합니다. 


4. google-services.json 파일을 다운로드 받아 안드로이드 스튜디오 프로젝트 안에 추가해 줍니다. 우선 Firebase에서 새로운 프로젝트를 생성합니다. 프로젝트 생성 후에 안드로이드 아이콘을 선택하면 아래와 같은 화면을 볼 수 있습니다. 


안드로이드 스튜디오에서 생성한 프로젝트의 패키지 명을 입력하고 앱 추가 버튼을 클릭 합니다. 

그러면 또 아래와 같은 이미지를 확인할 수 있는데 여기서 google-services.json 파일을 다운 받아 이미지에 있는 것 처럼 프로젝트 폴더 안에 넣어주도록 합니다. 


5. 안드로이드 스튜디오에서 빨간 테두리에 있는 버튼을 찾아 Gradle 싱크를 맞춥니다. 


6. AndroidManifest.xml 파일에 아래 코드를 추가합니다. 

  처음 서비스는 등록 토큰 생성, 순환, 업데이트를 처리하기 위해 추가한 것이고, 푸시 메시지를 특정 기기로 전송하거나 기기 그룹을 만드는 경우에 필요합니다.

  두번 째 서비스는 백그라운드에서 앱의 알림을 수신하는 것 이외에 다른 방식으로 메시지를 처리하려는 경우에 필요합니다. 포어그라운드 앱의 알림 수신, 데이터 페이로드 수신, 업스트림 메시지 전송 등을 수행하려면 이 서비스를 확장해야 합니다. 

 

 <service

    android:name=".MyFirebaseInstanceIDService">

    <intent-filter>

        <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>

    </intent-filter>

 </service>


 <service

    android:name=".MyFirebaseMessagingService">

    <intent-filter>

        <action android:name="com.google.firebase.MESSAGING_EVENT"/>

    </intent-filter>

 </service>



7. 프로젝트에 'MyFirebaseInstanceIDService' 와 'MyFirebaseMessageingService' 클래스 파일을 각 각 만들어 줍니다. 그리고 상속 받는 코드를 추가해 주면 됩니다. 


*MyFirebaseInstanceIDService.class

 

 public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {


    private static final String TAG = "MyFirebaseIIDService";


    @Override

    public void onTokenRefresh() {

        // Get updated InstanceID token.

        String refreshedToken = FirebaseInstanceId.getInstance().getToken();

        Log.d(TAG, "Refreshed token: " + refreshedToken);



        // If you want to send messages to this application instance or

        // manage this apps subscriptions on the server side, send the

        // Instance ID token to your app server.

        sendRegistrationToServer(refreshedToken);

    }

 private void sendRegistrationToServer(String token) {

        // TODO: Implement this method to send token to your app server.

    }

 }



*MyFirebaseMessageingService.class

 

 public class MyFirebaseMessagingService extends FirebaseMessagingService {


    private static final String TAG = "MyFirebaseMsgService";

   

    @Override

    public void onMessageReceived(RemoteMessage remoteMessage) {

        // [START_EXCLUDE]

        // There are two types of messages data messages and notification messages. Data messages are handled

        // here in onMessageReceived whether the app is in the foreground or background. Data messages are the type

        // traditionally used with GCM. Notification messages are only received here in onMessageReceived when the app

        // is in the foreground. When the app is in the background an automatically generated notification is displayed.

        // When the user taps on the notification they are returned to the app. Messages containing both notification

        // and data payloads are treated as notification messages. The Firebase console always sends notification

        // messages. For more see: https://firebase.google.com/docs/cloud-messaging/concept-options

        // [END_EXCLUDE]



        // TODO(developer): Handle FCM messages here.

        // Not getting messages here? See why this may be: https://goo.gl/39bRNJ

        Log.d(TAG, "From: " + remoteMessage.getFrom());



        // Check if message contains a data payload.

        if (remoteMessage.getData().size() > 0) {

            Log.d(TAG, "Message data payload: " + remoteMessage.getData());

        }



        // Check if message contains a notification payload.

        if (remoteMessage.getNotification() != null) {

            Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());

        }



        // Also if you intend on generating your own notifications as a result of a received FCM

        // message, here is where that should be initiated. See sendNotification method below.

    }

    // [END receive_message]



    /**

    * Create and show a simple notification containing the received FCM message.

    *

    * @param messageBody FCM message body received.

    */

    private void sendNotification(String messageBody) {

        Intent intent = new Intent(this, MainActivity.class);

        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,

                PendingIntent.FLAG_ONE_SHOT);



        Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);

        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)

                .setSmallIcon(R.drawable.ic_stat_ic_notification)

                .setContentTitle("FCM Message")

                .setContentText(messageBody)

                .setAutoCancel(true)

                .setSound(defaultSoundUri)

                .setContentIntent(pendingIntent);



        NotificationManager notificationManager =

                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);



        notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());

    }

}



8. 앱 설정은 끝났습니다. 이제 Firebase에서 메시지를 보내서 수신이 잘 되는지 테스트 하면 됩니다. 

Firebase 콘솔로 가서 좌측 메뉴 중간 부분에 Notifications를 들어가면 메시지를 보낼 수 있습니다. 


메시지가 잘 도착하면 이로써 Application 단은 수신 준비는 끝났습니다. 

물론 토큰을 생성하거나 메시지를 처리하는 부분은 추가 구현이 필요하지만 기본적인 동작은 끝난 겁니다. 


다음은 서버를 구성해서 푸시 메시지를 전송하는 법을 알아보도록 하겠습니다. 

당겨서 새로고침, Pull to refresh


당겨서 새로고침을 위해서 SwipeRefreshLayout가 필요하며 이는 Android support library v4에 포함되어 있다. 

먼저 gradle에 support v4 라이브러리를 추가한다. 

  

  dependencies {

     . . .

    complie 'com.android.support:support-v4:25.1.0'

  }



다음은 새로고침을 적용할 뷰를 SwipeRefreshLayout로 감싼다. 아마 대부분 ListView나 RecyclerView가 될 것이다. 


 <android.support.v4.widget.SwipeRefreshLayout

         android:layout_width="match_parent"

         android:layout_height="match_parent"

         android:id="@+id/sr_layout">


        <ListView

            . . . 

        />


 </android.support.v4.widget.SwipeRefreshLayout>



이제 새로고침에 대한 반응을 할 OnRefreshListener 인터페이스를 등록해야 한다. 등록하기에 앞서 SwipeRefreshLayout 객체를 만들자. 

 

 SwipeRefreshLayout swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.sr_layout);


 swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {

       @Override

       public void onRefresh() {

            // 새로고침 할 작업

            swipeRefreshLayout.setRefreshing(false);

       }

 }

위에서 setRefreshing(false)가 없으면 새로고침 아이콘이 사라지지 않는다. 


현재 구글은 Pull To Refresh UI에 대한 새로운 스타일을 공개했고, 이와 관련해서 Naver D2에서 오픈소스로

공개한 주소를 공유한다. 

https://github.com/nhnopensource/android-pull-to-refresh

Fragment Refresh, 프래그먼트 새로고침


프래그먼트에 리스트뷰를 사용하거나 화면을 새로고침해야 하는 경우가 있다. 

보통 액티비티에서 리스트뷰를 사용할 때 리스트뷰의 아이템에 변경이 발생했을 시

리스트 항목을 새로고침 하려고 한다. 이 때 사용하는 것이 리스트뷰의 adapter을 사용하여

새로고침 하는 방법이다. 

adapter.notifyDataSetChanged(); 를 사용하여 새로고침 한다. 


하지만!!

프래그먼트에서 리스트뷰를 새로고침하기 위해 아무리 위의 메서드를 호출하더라도

리스트뷰의 새로고침을 볼 수 없다. 

프래그먼트 화면을 새로 고침해줘야하는데 이때 FragmentTransaction을 사용한다. 

FragmentTransaction ft = getFragmentManager().beginTransaction();

ft.detach(this).attach(this).commit(); 을 하면 화면이 바뀌는 것을 확인할 수 있다. 

strings.xml

문자열, 문자열 배열, 수량문자열(복수형)을 지원합니다. 안드로이드 프로젝트 생성시 기본적으로 strings.xml을 사용하여 문자열에 대한 정의 및 수정을 하면 됩니다. 하지만 필요할 시 strings.xml 말고 xml 파일을 새로 추가하여 사용할 수 있습니다. 아래는 간단한 사용 방법을 적은 것이고 자세한 사용법을 알고 싶다면 개발자 페이지를 참고하면 됩니다. 

참고: https://developer.android.com/guide/topics/resources/string-resource.html


한줄 띄워쓰기

한줄 띄우고 싶은 부분에 \n 을 추가하면 됩니다. 

 다음에는 한 줄 띄워 쓰기 \n 다음줄 부터 시작함


볼드체, 이텔릭체 적용하기

적용하고자 하는 텍스트 앞 뒤로 태그를 추가하면 됩니다. 

 <b> 여기는 두껍게 </b>

 <i> 여기는 기울여 쓰기 </i>

 <u> 여기는 및줄 </u>


Fragment


  Fragment는 Activity 내에서 사용할 수 있는 Layer라고 생각하시면 이해하기 쉬울 것 같습니다. 여러개의 Fragment를 하나의 Activity에 조합하여 사용하여 창이 여러개인 UI를 만들 수 있고, 하나의 Fragment를 여러 Activity에서 재사용 할 수 있습니다. 

  Fragment는 자체 생명 주기를 가지고 있습니다. Fragment는 항상 Activity내에 포함되어 있어야 하고, 생명주기는 Host Activity의 생명 주기에 직접적으로 영향을 받습니다. 원래 도화지가 없어졌는데 그 위에 Layer가 존재할 수 없겠죠? 예를 들어 Activity가 일시 정지 되는 경우 Activity안에 있는 모든 Fragment도 일시정지 되며 소멸 시에도 동일합니다. Fragment 트랜젝션을 수행할 때 Activity가 관리하는 백 스택에도 추가할 수 있습니다. 

  아래 그림은 Fragment를 사용하여 태블릿에서 조합하여 사용하거나, 스마트폰에서 분리해서 사용할 수 있다는 설명을 보여줍니다. 이하 더 자세한 설명은 개발자 페이지에서 확인해주세요. (https://developer.android.com/guide/components/fragments.html?hl=ko)

[출처: Google Developer]




구현


  Fragment는 일반적으로 Activity의 사용자 인터페이스의 일부로 사용되며 자체 레이아웃으로 Activity에 표현 가능합니다. 

Fragment에서 레이아웃을 사용하려면 반드시 onCreateView() 콜백 메서드를 구현해야 합니다. 그리고 이 메서드는 반드시 View를 반환해야 합니다. 


  다음 예는 Fragment의 서브 클래스로 example_fragment.xml 파일로부터 레이아웃을 읽어 옵니다. 

public static class ExampleFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.example_fragment, container, false);
    }
}

  onCreateView()로 전달된 container 매개변수가 상위 ViewGroup이며 이는 Activity의 레이아웃으로부터 온 것입니다. 이 안에 Fragment 레이아웃이 삽입 됩니다. savedInstanceState는 true, false로 값을 정할 수 있는데 false 일 때는 레이아웃에 중복된 ViewGroup을 생성하지 못하게 하는 것입니다. 간단하게 말하면 각 각의 Fragment 레이아웃 중복 없이 사용하고 싶다면 savedInstanceState를 false로 사용해야 합니다. 



Fragment 관리


  Activity내에서 Fragment를 사용하기 위해서 FragmentManager을 사용해야 합니다. Activity에서 getFragmentManager()를 호출하면 됩니다. 

  ● FragmentManager fragmentManager = getFragmentManager();


  FragementManager을 통해 할 수 있는 일중에 몇 가지를 예를 들어보겠습니다. 

  ● Activity내에 존재하는 Fragment를 findFragmentById()로 가져오거나 findFragmentByTag()로 가져올 수 있습니다. 

  ● popBackStack()을 사용하여 Fragment를 백스택에서 꺼낼 수 있습니다. 

  ● 백스택 변경 내용이 있는지 확인하기 위해 addOnBackStackChangedListener()로 리스너를 등록할 수 있습니다. 



Fragment 트랜젝션


  FragmentTransaction의 인스턴스를 FragmentManager로부터 가져오는 방법은 아래와 같습니다. 

  ● FragmentManager fragmentManager = getFragmentManager(); 

  ● FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();


Transaction에서 add(), remove(), replace()와 같은 메서드를 사용할 수 있습니다. 그리고 마지막에 반드시 commit()를 호출해야 합니다. commit() 호출 전 해야할 것이 있는데 addToBackStack()입니다. fragment transaction은 activity가 관리하며 이를 통해 사용자가 back 버튼을 이용하여 이전 fragment로 돌아갈 수 있습니다. 

// Create new fragment and transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();

// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);

// Commit the transaction
transaction.commit();


Activity와의 통신


  Fragment에서 activity의 인스턴스에 엑세스 하려면 getActivity()를 사용합니다.

  ● View listView = getActivity().findViewById(R.id.list);


  반대로 activity에서도 fragment 안의 메서드를 호출할 수 있습니다. 이 때 FragmentManager을 사용하여 Fragment에 대한 참조를 가져와야 합니다. 

  ● ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);



정리

  Fragment는 activity위에 레이아웃을 그릴 수 있는 ViewGroup 중 하나입니다. 따라서 activity와 생애를 같이 합니다. Activity에서 Fragment를 다루려면 FragmentManager가 필요하고 getFragmentManager()을 통해 사용이 가능합니다. 그리고 fragment를 추가, 제거 할 때 즉 transaction이 발생할 때 FragmentTransaction을 사용합니다. 


  다음 글에서는 실제 코드를 가지고 어떻게 fragment를 활용했는지 알아보도록 하겠습니다. 

Android App 정보 가져오기(App icon, package, app 이름)


Android 단말에서 App icon, App 이름, Package 이름 등을 가지고 오려고 하면 다음과 같이 하면 된다. 


[package]

final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);

final List pkgAppsList = getPackageManager().queryIntentActivities(
mainIntent, 0);
String[] requestedPermissions = null;
String packageName = "";

for (Object obj : pkgAppsList) {
ResolveInfo resolveInfo = (ResolveInfo) obj;
PackageInfo packageInfo = null;
try {
packageInfo = getPackageManager().getPackageInfo(
resolveInfo.activityInfo.packageName,
PackageManager.GET_PERMISSIONS);

requestedPermissions = packageInfo.requestedPermissions;
packageName = packageInfo.packageName;

} catch (PackageManager.NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}


[App Title]

String appName = (String) getPackageManager()

.getApplicationLabel(getPackageManager()

.getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES));


[Icon]

appIcon = getPackageManager().getApplicationIcon(packageName);


네트워크 상태를 확인하고, GPS의 상태를 확인하는 코드


우선 네트워크 상태를 확인하는 코드

boolean isOnline = false;

try {
		ConnectivityManager conMan = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
			
		State wifi = conMan.getNetworkInfo(1).getState();
		if(wifi == NetworkInfo.State.CONNECTED) {
			isOnline = true;
		}
			
		State mobile = conMan.getNetworkInfo(0).getState();
		if(mobile == NetworkInfo.State.CONNECTED) {
			isOnline = true;
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
}


GPS 상태를 확인하는 코드

boolean gpsEnable = false;
LocationManager manager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
if(manager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
	gpsEnable = true;
}


AwesomeNavigation.zip


예제는 위에 있는 압축파일 받아서 확인해주세요~


  Navigation Drawer에 대해서 쉽고, 알고 싶은 것을 속 시원히 알려드릴게요. 우선 기본적인 프로젝트를 이클립스로 생성했다면 아래와 같은 소스를 확인할 수 있습니다. 

package com.nexthops.awesomeweather;

import android.app.Activity;

import android.app.ActionBar;
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.support.v4.widget.DrawerLayout;
import android.widget.ArrayAdapter;
import android.widget.TextView;


public class MainActivity extends Activity
        implements NavigationDrawerFragment.NavigationDrawerCallbacks {

    /**
     * Fragment managing the behaviors, interactions and presentation of the navigation drawer.
     */
    private NavigationDrawerFragment mNavigationDrawerFragment;

    /**
     * Used to store the last screen title. For use in {@link #restoreActionBar()}.
     */
    private CharSequence mTitle;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mNavigationDrawerFragment = (NavigationDrawerFragment)
                getFragmentManager().findFragmentById(R.id.navigation_drawer);
        mTitle = getTitle();

        // Set up the drawer.
        mNavigationDrawerFragment.setUp(
                R.id.navigation_drawer,
                (DrawerLayout) findViewById(R.id.drawer_layout));
    }

    @Override
    public void onNavigationDrawerItemSelected(int position) {
        // update the main content by replacing fragments
        FragmentManager fragmentManager = getFragmentManager();
        fragmentManager.beginTransaction()
                .replace(R.id.container, PlaceholderFragment.newInstance(position + 1))
                .commit();
    }

    public void onSectionAttached(int number) {
        switch (number) {
            case 1:
                mTitle = getString(R.string.title_section1);
                break;
            case 2:
                mTitle = getString(R.string.title_section2);
                break;
            case 3:
                mTitle = getString(R.string.title_section3);
                break;
        }
    }

    public void restoreActionBar() {
        ActionBar actionBar = getActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
        actionBar.setDisplayShowTitleEnabled(true);
        actionBar.setTitle(mTitle);
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        if (!mNavigationDrawerFragment.isDrawerOpen()) {
            // Only show items in the action bar relevant to this screen
            // if the drawer is not showing. Otherwise, let the drawer
            // decide what to show in the action bar.
            getMenuInflater().inflate(R.menu.main, menu);
            restoreActionBar();
            return true;
        }
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * A placeholder fragment containing a simple view.
     */
    public static class PlaceholderFragment extends Fragment {
        /**
         * The fragment argument representing the section number for this
         * fragment.
         */
        private static final String ARG_SECTION_NUMBER = "section_number";

        /**
         * Returns a new instance of this fragment for the given section
         * number.
         */
        public static PlaceholderFragment newInstance(int sectionNumber) {
            PlaceholderFragment fragment = new PlaceholderFragment();
            Bundle args = new Bundle();
            args.putInt(ARG_SECTION_NUMBER, sectionNumber);
            fragment.setArguments(args);
            return fragment;
        }

        public PlaceholderFragment() {
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_main, container, false);
            return rootView;
        }

        @Override
        public void onAttach(Activity activity) {
            super.onAttach(activity);
            ((MainActivity) activity).onSectionAttached(
                    getArguments().getInt(ARG_SECTION_NUMBER));
        }
    }

}

  어디를 손대야 내가 원하는데로 화면 전환이 될지가 궁금해 질텐데요. Navigation Drawer 프로젝트는 살펴보면 Fragment라는 녀석이 있습니다. 그래서 우리는 Activity를 사용하지 않고 Fragment를 사용할 거에요. 


  보여주고 싶은 화면을 두 개만 만들어 보겠습니다. 우선은 액티비티를 두개 추가해 줍니다. 그리고 소스를 수정할거에요. 

package com.example.awesomenavigation;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

public class FirstItemFragment extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.first_item_fragment);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.first_item, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle action bar item clicks here. The action bar will
		// automatically handle clicks on the Home/Up button, so long
		// as you specify a parent activity in AndroidManifest.xml.
		int id = item.getItemId();
		if (id == R.id.action_settings) {
			return true;
		}
		return super.onOptionsItemSelected(item);
	}
}



  많이 보던 소스입니다. 여기서 아래와 같이 수정 할게요. 

package com.example.awesomenavigation;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class FirstItemFragment extends Fragment {

	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		
		View view = inflater.inflate(R.layout.first_item_fragment, container, false);
		
		return view;
	}
}

  Activity를 Fragment로 수정했습니다. 이렇게 더 추가할 화면을 액티비티로 생성한 다음 위와 같이 Fragment로 수정합니다. 다시 메인 액티비티로 넘어가보도록 하겠습니다. 이제 거의 다 됐어요~

package com.example.awesomenavigation;

import android.app.Activity;

import android.app.ActionBar;
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.support.v4.widget.DrawerLayout;
import android.widget.ArrayAdapter;
import android.widget.TextView;


public class MainActivity extends Activity
        implements NavigationDrawerFragment.NavigationDrawerCallbacks {

    /**
     * Fragment managing the behaviors, interactions and presentation of the navigation drawer.
     */
    private NavigationDrawerFragment mNavigationDrawerFragment;

    /**
     * Used to store the last screen title. For use in {@link #restoreActionBar()}.
     */
    private CharSequence mTitle;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mNavigationDrawerFragment = (NavigationDrawerFragment)
                getFragmentManager().findFragmentById(R.id.navigation_drawer);
        mTitle = getTitle();

        // Set up the drawer.
        mNavigationDrawerFragment.setUp(
                R.id.navigation_drawer,
                (DrawerLayout) findViewById(R.id.drawer_layout));
    }

    @Override
    public void onNavigationDrawerItemSelected(int position) {
        // update the main content by replacing fragments
    	Fragment fragment;
    	FragmentManager fragmentManager = getFragmentManager();
    	switch(position) {
    	case 0:
    		fragment = new FirstItemFragment();
    		break;
    		
    	case 1:
    		fragment = new SecondItemFragment();
    		break;
    	default:
    		fragment = new FirstItemFragment();
    	}
    	
    	fragmentManager.beginTransaction()
    	.replace(R.id.container, fragment)
    	.commit();
    }

    public void onSectionAttached(int number) {
        switch (number) {
            case 1:
                mTitle = getString(R.string.title_section1);
                break;
            case 2:
                mTitle = getString(R.string.title_section2);
                break;
            case 3:
                mTitle = getString(R.string.title_section3);
                break;
        }
    }

    public void restoreActionBar() {
        ActionBar actionBar = getActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
        actionBar.setDisplayShowTitleEnabled(true);
        actionBar.setTitle(mTitle);
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        if (!mNavigationDrawerFragment.isDrawerOpen()) {
            // Only show items in the action bar relevant to this screen
            // if the drawer is not showing. Otherwise, let the drawer
            // decide what to show in the action bar.
            getMenuInflater().inflate(R.menu.main, menu);
            restoreActionBar();
            return true;
        }
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * A placeholder fragment containing a simple view.
     */
    public static class PlaceholderFragment extends Fragment {
        /**
         * The fragment argument representing the section number for this
         * fragment.
         */
        private static final String ARG_SECTION_NUMBER = "section_number";

        /**
         * Returns a new instance of this fragment for the given section
         * number.
         */
        public static PlaceholderFragment newInstance(int sectionNumber) {
            PlaceholderFragment fragment = new PlaceholderFragment();
            Bundle args = new Bundle();
            args.putInt(ARG_SECTION_NUMBER, sectionNumber);
            fragment.setArguments(args);
            return fragment;
        }

        public PlaceholderFragment() {
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_main, container, false);
            return rootView;
        }

        @Override
        public void onAttach(Activity activity) {
            super.onAttach(activity);
            ((MainActivity) activity).onSectionAttached(
                    getArguments().getInt(ARG_SECTION_NUMBER));
        }
    }

}


  50번 째 줄 부터 처음 MainActivity와 변경된 부분을 확인하면 되요. Fragment를 사용하여 Activity위에서 그려지는 부분을 변경하는 거에요. Fragment에 대한 이해가 없는 분이라면 Fragment에 대한 이해부터 하시고 이것을 보시면 더 쉽게 이해할 수 있어요. 

  화면을 꾸며주고 싶은 부분은 Fragment에서 추가하면됩니다. Activity와 생명주기도 조금 다르고, 사용하는 방법도 조금 다르니 꼭 Fragment에 대해 이해하도록 해요~


「 Tip!! 

1. 안드로이드에서 제공하는 기본 Navigation Drawer 프로젝트를 생성한다. 

2. 구성하고 싶은 화면을 Activity로 생성한다. 

3. Activity 를 Fragment 로 변경한다. 

4. MainActivity에서 Fragment를 replace 한다. 그러면 원하는 화면으로 휙휙 전환된다. 

아래의 설명은 GCM 등록과 관련된 코드에 문제가 없다는 전제하에 설명한 글입니다.


GCM을 구현 한 것에서 모든 문제가 없는데, 디버깅을 보니 아래와 같이 딱! 하니 에러코드가 나온다면 한번 의심해 봐야할 것이 있습니다.



사용자가 App을 설치하고 실행할 때 '백그라운드 데이터 제한' 설정을 해 놓았다면, 혹은 'Google Play 서비스' 항목에 해당해 '백그라운드 데이터 제한'을 했다면 그리고 사용하는 PUSH Service가  GCM 하나 밖에 없다면 저런 에러 메시지를 볼 수 있습니다.

문제를 해결할 수 있는 방법은 '백그라운드 데이터 제한'을 풀거나, GCM 이외의 Push Server를 두고 catch 문 안에 두 번째 Push Server로부터 Push Key를 가져오게 하는 방법 외엔 없습니다.


아래 그림은 '백그라운드 데이터 제한' 설정과 관련된 이미지입니다. 아래 그림에서 제한 설정을 끄면 GCM 서비스를 정상적으로 사용할 수 있습니다. 그리고 이 문제는 앱을 처음 설치할 시에만 발생합니다. 



+ Recent posts