우동우동우's note

[Android] Fragment 안에 MapView 넣기 본문

Java & Android

[Android] Fragment 안에 MapView 넣기

우동우동우 2012. 8. 8. 00:18

Fragment의 매력은 안드로이드 개발자라면 누구나 느낄 것이다. 

Fragment를 활용하면 ActivitiyGroup이 하는 일을 Activity에서 대신할 수 있고, onActivityForResult 메서드도 각 메서드에서 구현을 하면 되니깐 말이다. 

그런데 한가지 아쉬운 점이 있는데... MapView를 Fragment에서 사용할 수 없는 것이다.

처음에 뭣도 모르고 Fragment에 MapView를 넣고서는 "왜 안되지?"를 삼십번 넘게 생각하다가 MapActivity에 넣어야 한다는 걸 깨닳았다. 

그 후에 MapFragment를 찾았지만.. 없었다는...  ㅠㅠㅠㅠ

그래서 Lib 소스를 보고 바꾸자라는 결론이... 그렇게 나의 삽질(?)은 시작되었다... 

삽질에 대한 얘기는 중요하시 않으므로 하지는 않겠다. 


이제 본론으로 들어가도록 하겠다. 우선 android-support-v4-sources.jar의 소스 파일을 받아와야 한다. 소스는 "<ANDROID_SDK>/extras/android/support/v4/src/java"에 있다. 안에 보면 android라는 폴더가 있을 것이다. 그걸 복사해서 프로젝트 안에 넣자. 그러면 아래 그림과 같이 나타날 것이다. 여기서 android.support.v4.app을 제외하고 전부 지우자. 지웠을 때 어쩌면 컴파일 에러가 날수도 있다 이에 대한 해결 책은 나중에 알려주겠다. 


다 지웠다면 다음으로 android.support.v4.app의 패키지 명을 android.support.v4.map.app으로 변경하자. 이렇게 변경하는 이유는 android-support-v4-sources.jar에서 선언 된 다른 파일과 겹치지 않기 위해서 이다. 이렇게 수정을 하고 난다음에는 아래의 파일을 제외하고 전부 지우자. 

  • BackStackRecord.java

  • FragmentActivity.java

  • Fragment.java

  • FragmentManager.java

  • FragmentTransaction.java

  • LoaderManager.java

  • NoSaveStateFrameLayout.java

  • SuperNotCalledException.java


그리고나서 나는 아래와 같이 파일이름을 전부 변경하였다. 나중에 사용할때 혼선을 빚지 않기 위해서이다. 파일이름을 변경할 때 Reference도 동일하게 변경하도록 하는 방법은 다들 안다고 가정하고 넘어가겠다. (파일 내부에 보면 내부클래스를 선언해놓았다. 본인은 몇몇 내부 클래스 이름도 같이 변경했다. 지금 생각해보면 변경하지 않아도 될것으로 생각된다.)

  • BackStackRecord.java

  • MapFragmentActivity.java

  • MapSupportFragment.java

  • MapSupportFragmentManager.java

  • MapSupportFragmentTransaction.java

  • MapSupportLoaderManager.java

  • NoSaveStateFrameLayout.java

  • SuperNotCalledException.java

이제 다음으로 프로젝트에 android-support-v4-sources.jar파일을 import하면된다. 하는 방법은 Fragment를 써봤다면 안다고 가정하고 넘어가겠다. 그러면 컴파일 에러가 나타나있는 부분이 전부 사라질 것이다. 


이제 마지막 작업이다. MapFragmentActivity.java 파일을 열어보자.  파일을 열고 수정할 부분은 한군데 이다. 아래와 같이 변경해주자. 

MapFragmentActivity extends Activity --> MapFragmentActivity extends MapActivity

/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.support.v4.map.app;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;

import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Parcelable;
import android.support.v4.util.SparseArrayCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;

import com.google.android.maps.MapActivity;

public abstract class MapFragmentActivity extends MapActivity {
   중략... 
}

자 이제  예제 프로젝트를 만들어 보자. 


activity_main.xml



    

    




MainActivity.java

package seo.dongu.mapfragment;

import android.os.Bundle;
import android.support.v4.map.app.MapFragmentActivity;
import android.support.v4.map.app.MapSupportFragment;
import android.support.v4.map.app.MapSupportFragmentManager;
import android.support.v4.map.app.MapSupportFragmentTransaction;
import android.view.Menu;

public class MainActivity extends MapFragmentActivity {

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

        MapSupportFragment fragment = new TestFragment();
		MapSupportFragmentManager fragmentManager = getSupportFragmentManager();
		MapSupportFragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
		fragmentTransaction.add(R.id.container, fragment);
		fragmentTransaction.commit();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

	@Override
	protected boolean isRouteDisplayed() {
		return false;
	}

    
}


map_test.xml




    



TestFragment.java

package seo.dongu.mapfragment;

import android.os.Bundle;
import android.support.v4.map.app.MapSupportFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;

public class TestFragment extends MapSupportFragment {

	static LinearLayout v;
	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		if(v == null){
			v = (LinearLayout) inflater.inflate(R.layout.map_test, container, false);
		}
		return v;
	}

	@Override
	public void onDestroyView() {
		FrameLayout parent = (FrameLayout) v.getParent();
		parent.removeView(v);
		super.onDestroyView();
	}	
}


AndroidManifest.xml




    

    

    
        

        
            
                

                
            
        
    



여기서 한가지 특이사항은 TestFragment.java 에서 view를 전역 변수로 선언 해주고 생성할 때 저장하고 onDestryView에서 제거해준다는 점이다. 이렇게 하는 이유는 동일한 Activity내에서 Fragment를 교환하는 일이 많은데 이때 MapView는 MapActivity내부에 2개이상 넣게될 경우 에러가 발생하게 된다. 이를 해결하기위해서 전역으로 View를 저장해주고 onDestroyView에서 Parent에서 removeView를 해주는 것이다. 이에 대해서 의문을 품는 사람들이 있을 텐데 Fragment내에서 자체적으로 container에서 View를 제거해주는 것 아니냐고 라고 말이다. 맞다 하지만 View의 Parent는 여전히 존재한다. 이유는 Fragment를 분석하면서 알게된 사실인데 View를 만들면서 FrameLayout으로 한번 View를 포장하고 그 다음에 만든 FrameLayout을 넣는다. 이러한 방식 때문에 onDestroyView에서 Parent를 FrameLayout으로 형변환을 해주고 해당 View에서 제거를 해주는 것이다. 



자 이제 설명은 끝이다. 혹시.. 너무 복잡하다고 생각하실 수 있지만.. 그래도 이 방법이 그나마 간단한 방법이 아닐까 생각한다. 내가 작업한 것의 아쉬운 점은 라이브러리의 다른 기능을 지원하지 않는 문제점이다. 할려면 할 수도 있다... 할려면 전부 바꿔야 하니깐... 시간의 문제가 아닐까 생각한다. 그 정도로 가치있는 개발은 아니라고 생각하기 때문에... 뭐 Android에서  MapView를 지원하도록 만들면 끝이니깐... 아무튼 여기서 나의 글을 마친다. 소스는 아래의 링크에 있다. 


MapFragment.zip



Comments