본문 바로가기

Programming/과거포스팅

안드로이드 구글맵 말풍선 이미지(Android GoogleMap Balloon Image)

오늘은 구글맵에 징표와 징표를 클릭시 말풍선을 띄어보도록 하겠습니다.

말풍선은 외국의 오픈 소스를 사용했구요. 레이아웃을 조금 수정하여 이미지를 추가하는 작업을 하였습니다.

우선 코드가 난잡합니다. 제가 안드로이드를 이제 막 시작해서 아직 많이 서툴러서 우선 코드는 신경쓰지 않았구요

기능이 되는지 여부만 보는 중이라 잡다하거나 좀 더러운 부분은 이 포스팅을 보시는 분들이 적절하게 수정해서 사용하시기 바랍니다.


자 오늘 해볼 과제의 결과물 부터 볼까요?


맵 마커를 선택시 마커에 맞는 이미지의 drawable 값을 넘겨줘서 다른 이미지를 띄어보는 것입니다.


다 필요없다 소스만 달라하시는분은 요거 퍼가시구요


GCCollege.zip









작업한경은 2.3.3 진저브레드입니다.


소스 몇몇만 살펴보겠습니다. 


balloon_overlay.xml  오픈소스의 말풍선 레이아웃입니다. 밑줄 친 이미지뷰를 추가해 줍니다.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content"
    android:orientation="horizontal" 
    android:paddingBottom="35dip"
    android:paddingLeft="10dip" 
    android:minWidth="200dip" 
    android:id="@+id/balloon_main_layout"
    android:background="@drawable/balloon_overlay_bg_selector" 
    android:paddingTop="0dip"
    android:paddingRight="0dip">

    <AbsoluteLayout 
    android:layout_width="100px"
    android:layout_height="100px"
    android:gravity="center"
    android:orientation="vertical" >

        <ImageView
            android:id="@+id/ballon_img"
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            android:layout_x="0dp"
            android:layout_y="4dp"
            android:src="@drawable/balloon_overlay_bg_selector" />

    </AbsoluteLayout>
        
    <LinearLayout
        android:id="@+id/balloon_inner_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical"
        android:paddingTop="10dip" >

        <TextView
            android:id="@+id/balloon_item_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="0.65"
            android:text="balloon_item_title"
            android:textColor="#FF000000"
            android:textSize="16dip" />

        <TextView
            android:id="@+id/balloon_item_snippet"
            android:layout_width="132dp"
            android:layout_height="fill_parent"
            android:layout_weight="1.76"
            android:text="balloon_item_snippet"
            android:textSize="12dip" >

        </TextView>
    </LinearLayout>

    <ImageView
        android:id="@+id/close_img_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingBottom="10dip"
        android:paddingLeft="10dip"
        android:paddingRight="8dip"
        android:paddingTop="8dip"
        android:src="@drawable/balloon_overlay_close" />

</LinearLayout>

CollegeMapList.java 맵 액티비티입니다. 주석으로 설명하겠습니다

package com.soyeong.Activity; import java.util.List; import android.graphics.drawable.Drawable; import android.os.Bundle; import com.google.android.maps.GeoPoint; import com.google.android.maps.MapController; import com.google.android.maps.MapView; import com.google.android.maps.Overlay; import com.google.android.maps.OverlayItem; import com.soyeong.common.CollegeMapList; import com.soyeong.common.MapParam; import com.soyeong.gccollege.R; import com.soyeong.util.MyItemizedOverlay; public class MapActivity extends com.google.android.maps.MapActivity{ private MapView mapView; private MapController mapController; private List<Overlay> mapOverlays; private Drawable drawable; private MyItemizedOverlay itemOverlay; private OverlayItem overlayItem; private GeoPoint geoPoint; List list; double latitude = 35.67397; double longitude = 127.91236; private String mapFlag = ""; private String param = ""; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.map); param = MapParam.mapFlag; System.out.println("param : " + param); mapView = (MapView)findViewById(R.id.map_view); mapView.setBuiltInZoomControls(true); mapOverlays = mapView.getOverlays(); drawable = getResources().getDrawable(R.drawable.marker); itemOverlay = new MyItemizedOverlay(drawable, mapView); if(param.equals("college")) -----------------대학을 선택했을때 맵 설정입니다. { latitude = 35.67397; longitude = 127.91236; CollegeMapList mapList = new CollegeMapList(); ------------------좌표와 , 정보가 있는 리스트입니다. 파일을 참조하세요. list = mapList.getList(); } else if(param.equals("beer")) { } for(int i=0, j=0; i < list.size();) -------------------리스트에 저장한 파일을 리스트를 돌면서 좌표와 타이틀 정보를 뿌려줍니다. { GeoPoint geoPoint = new GeoPoint((int)(Double.parseDouble(list.get(i++).toString()) * 1E6), (int)(Double.parseDouble(list.get(i++).toString()) * 1E6)); overlayItem = new OverlayItem(geoPoint, list.get(i++).toString(), list.get(i++).toString()); itemOverlay.addOverlay (overlayItem, "college", j++ ); ------- "이미지를 뽑기 위해 타입과 인덱스를 추가 해 줍니다."

} mapOverlays.add(itemOverlay); mapController = mapView.getController(); geoPoint = new GeoPoint((int)(latitude * 1E6), (int)(longitude * 1E6)); mapController.animateTo(geoPoint); mapController.setZoom(19); mapView.invalidate(); } @Override protected boolean isRouteDisplayed() { // TODO Auto-generated method stub return false; } }


MyItemizedOverlay.java 오픈 소스중 첫번째 접근하는 파일입니다.    

/***  * Copyright (c) 2010 readyState Software Ltd  *  * 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 com.soyeong.util; import java.util.ArrayList; import android.content.Context; import android.content.Intent; import android.graphics.drawable.Drawable; import android.widget.Toast; import com.google.android.maps.MapView; import com.google.android.maps.OverlayItem; public class MyItemizedOverlay extends BalloonItemizedOverlay<OverlayItem> { private ArrayList<OverlayItem> m_overlays = new ArrayList<OverlayItem>(); private Context c; public MyItemizedOverlay(Drawable defaultMarker, MapView mapView) { super(boundCenter(defaultMarker), mapView); c = mapView.getContext(); } public void addOverlay(OverlayItem overlay, String id, int idx) { ------------ 생성자에 인덱스와 아이디를 추가해줍니다. m_overlays.add(overlay); super.id = id; super.idx = idx; -------------------super이니 상속을 해준 부모클래스에 구현합니다. populate(); } @Override protected OverlayItem createItem(int i) { return m_overlays.get(i); } @Override public int size() { return m_overlays.size(); } @Override protected boolean onBalloonTap(int index) { Toast.makeText(c, "onBalloonTap for overlay index " + index, Toast.LENGTH_LONG).show(); return true; } }

BalloonItemizedOverlay.java MyItemizedOverlay클래스의 부모 클래스입니다. 밑줄 친 부분만 수정하시면 됩니다.

/***  * Copyright (c) 2010 readyState Software Ltd  *  * 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 com.soyeong.util; import java.lang.reflect.Method; import java.util.List; import android.content.Intent; import android.graphics.drawable.Drawable; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; import android.view.ViewGroup.LayoutParams; import android.widget.ImageView; import com.google.android.maps.GeoPoint; import com.google.android.maps.ItemizedOverlay; import com.google.android.maps.MapController; import com.google.android.maps.MapView; import com.google.android.maps.Overlay; import com.google.android.maps.OverlayItem; import com.soyeong.gccollege.R; /**  * An abstract extension of ItemizedOverlay for displaying an information balloon  * upon screen-tap of each marker overlay.  *  * @author Jeff Gilfelt  */ public abstract class BalloonItemizedOverlay<Item> extends ItemizedOverlay<OverlayItem> { private MapView mapView; private BalloonOverlayView balloonView; private ImageView imageView; private View clickRegion; private int viewOffset; final MapController mc; protected String id; ------------------------ id와 index를 추가해 줍니다. 사실 idx를 뿌려주지 않아도 되는데 ㅡㅡ;하다보니 귀찮아서 놔둡니다. protected int idx; /**      * Create a new BalloonItemizedOverlay      *      * @param defaultMarker - A bounded Drawable to be drawn on the map for each item in the overlay.      * @param mapView - The view upon which the overlay items are to be drawn.      */ public BalloonItemizedOverlay(Drawable defaultMarker, MapView mapView) { super(defaultMarker); this.mapView = mapView; viewOffset = 0; mc = mapView.getController(); } /**      * Set the horizontal distance between the marker and the bottom of the information      * balloon. The default is 0 which works well for center bounded markers. If your      * marker is center-bottom bounded, call this before adding overlay items to ensure      * the balloon hovers exactly above the marker.      *      * @param pixels - The padding between the center point and the bottom of the      * information balloon.      */ public void setBalloonBottomOffset(int pixels) { viewOffset = pixels; } /**      * Override this method to handle a "tap" on a balloon. By default, does nothing      * and returns false.      *      * @param index - The index of the item whose balloon is tapped.      * @return true if you handled the tap, otherwise false.      */ protected boolean onBalloonTap(int index) { return false; } /* (non-Javadoc)      * @see com.google.android.maps.ItemizedOverlay#onTap(int)      */ @Override protected final boolean onTap(int index) { boolean isRecycled; final int thisIndex; GeoPoint point; thisIndex = index; point = createItem(index).getPoint(); if (balloonView == null) { balloonView = new BalloonOverlayView(mapView.getContext(), viewOffset, id ,idx); ----------- 여기에 아이디와 인덱스를 추가해 줍니다. clickRegion = (View) balloonView.findViewById(R.id.balloon_inner_layout); isRecycled = false; } else { isRecycled = true; } balloonView.setVisibility(View.GONE); List<Overlay> mapOverlays = mapView.getOverlays(); if (mapOverlays.size() > 1) { hideOtherBalloons(mapOverlays); } balloonView.setData(createItem(index), thisIndex); MapView.LayoutParams params = new MapView.LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, point, MapView.LayoutParams.BOTTOM_CENTER); params.mode = MapView.LayoutParams.MODE_MAP; setBalloonTouchListener(thisIndex); balloonView.setVisibility(View.VISIBLE); if (isRecycled) { balloonView.setLayoutParams(params); } else { mapView.addView(balloonView, params); } mc.animateTo(point); return true; } /**      * Sets the visibility of this overlay's balloon view to GONE.      */ private void hideBalloon() { if (balloonView != null) { balloonView.setVisibility(View.GONE); } } /**      * Hides the balloon view for any other BalloonItemizedOverlay instances      * that might be present on the MapView.      *      * @param overlays - list of overlays (including this) on the MapView.      */ private void hideOtherBalloons(List<Overlay> overlays) { for (Overlay overlay : overlays) { if (overlay instanceof BalloonItemizedOverlay<?> && overlay != this) { ((BalloonItemizedOverlay<?>) overlay).hideBalloon(); } } } /**      * Sets the onTouchListener for the balloon being displayed, calling the      * overridden onBalloonTap if implemented.      *      * @param thisIndex - The index of the item whose balloon is tapped.      */ private void setBalloonTouchListener(final int thisIndex) { try { @SuppressWarnings("unused") Method m = this.getClass().getDeclaredMethod("onBalloonTap", int.class); clickRegion.setOnTouchListener(new OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { View l = ((View) v.getParent()).findViewById(R.id.balloon_main_layout); Drawable d = l.getBackground(); if (event.getAction() == MotionEvent.ACTION_DOWN) { int[] states = {android.R.attr.state_pressed}; if (d.setState(states)) { d.invalidateSelf(); } return true; } else if (event.getAction() == MotionEvent.ACTION_UP) { int newStates[] = {}; if (d.setState(newStates)) { d.invalidateSelf(); } // call overridden method onBalloonTap(thisIndex); return true; } else { return false; } } }); } catch (SecurityException e) { Log.e("BalloonItemizedOverlay", "setBalloonTouchListener reflection SecurityException"); return; } catch (NoSuchMethodException e) { // method not overridden - do nothing return; } } }

BalloonOverlayView.java // 최종 뷰 화면입니다. 이제 넘긴 id와 index로 이미지를 뽑아보죠

/***  * Copyright (c) 2010 readyState Software Ltd  *  * 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 com.soyeong.util; import android.content.Context; import android.content.Intent; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import com.google.android.maps.OverlayItem; import com.soyeong.common.CollegeMapList; import com.soyeong.gccollege.R; /**  * A view representing a MapView marker information balloon.  * <p>  * This class has a number of Android resource dependencies:  * <ul>  * <li>drawable/balloon_overlay_bg_selector.xml</li>  * <li>drawable/balloon_overlay_close.png</li>  * <li>drawable/balloon_overlay_focused.9.png</li>  * <li>drawable/balloon_overlay_unfocused.9.png</li>  * <li>layout/balloon_map_overlay.xml</li>  * </ul>  * </p>  *  * @author Jeff Gilfelt  *  */ public class BalloonOverlayView extends FrameLayout { private LinearLayout layout; private TextView title; private TextView snippet; private int index; private ImageView imageView;--------------------이미지뷰를 만들어 줍니다. CollegeMapList collegeMapList = new CollegeMapList(); ---------------아이디에 따라서 불러올 데이터 설정인데 지금은 조건없이 했습니다.

/**      * Create a new BalloonOverlayView.      *      * @param context - The activity context.      * @param balloonBottomOffset - The bottom padding (in pixels) to be applied      * when rendering this view.      */ public BalloonOverlayView(Context context, int balloonBottomOffset, String id , int index) { super(context); this.index = index; setPadding(10, 10, 10, balloonBottomOffset); layout = new LinearLayout(context); layout.setVisibility(VISIBLE); LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); View v = inflater.inflate(R.layout.balloon_overlay, layout); title = (TextView) v.findViewById(R.id.balloon_item_title); snippet = (TextView) v.findViewById(R.id.balloon_item_snippet); imageView = (ImageView) v.findViewById(R.id.ballon_img); imageView.setImageResource(collegeMapList.getImageList(index));--------------------클래스에 저장한 값을 불러옵니다. 최초1회 실행입니다. ImageView close = (ImageView) v.findViewById(R.id.close_img_button); close.setOnClickListener(new OnClickListener() { public void onClick(View v) { layout.setVisibility(GONE); } }); FrameLayout.LayoutParams params = new FrameLayout.LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); params.gravity = Gravity.NO_GRAVITY; addView(layout, params); } /**      * Sets the view data from a given overlay item.      *      * @param item - The overlay item containing the relevant view data      * (title and snippet).      */ public void setData(OverlayItem item , int index) { -----------클릭시 셋팅해주는 부분입니다. 이제부턴 클릭할때마다 이메서드가 호출됩니다. System.out.println("index : " + index);------------인덱스를 추가해줍니다. layout.setVisibility(VISIBLE); if (item.getTitle() != null) { title.setVisibility(VISIBLE); title.setText(item.getTitle()); } else { title.setVisibility(GONE); } if (item.getSnippet() != null) { snippet.setVisibility(VISIBLE); snippet.setText(item.getSnippet()); } else { snippet.setVisibility(GONE); } imageView.setImageResource(collegeMapList.getImageList(index)); -- 추가한 부분입니다. 여기에서 이미지를 계속 다르게 셋팅해줍니다. }



자 이것으로 포스팅을 마치면서 말씀드립니다. 오픈소스말고 제가 짠 소스는 절대 좋은 소스가 아닙니다. 불필요한 부분도 너무 많고 기능구현만을 위해서

조잡하게 짯습니다. 오픈소스가 어떻게 돌아가는지에 대한 감만 잡으시는 용도로 사용하시고 이소스에 너무 의존하지 마세요..

앞으로 조금씩 깔끔하게 코딩하는 습관좀 길러야겠습니다 ㅠㅠ 게을러서 잘안돼네요