I was just looking at Google maps on my friend’s Android Cellphone;
He asked me a great question related to
info window that was “Can We People Customize info window?”
Neither of us ever had seen a customized info window. I just kept
thinking how it could be done If Google doesn’t provide us facilities to
customize info window. I tried a lot to make a custom window.
I was trying to put more than one view on info window. Info window
works only as one view that can be clicked only once as full. So Customization
of info info window means adding more than one view (Buttons, or any clickable
that will do some work).
I just tried it and succeed. Here are the steps or what I did.
(I assume everyone, who is referring to this post, has basic
knowledge about info window and map)
This is done in two parts-
First Part:
The first part is to catch the clicks on the buttons to do some action (I just showed toast on clicks). The idea is like this:
1.
We can keep a reference
to the custom info window created in the InfoWindowAdapter.
2.
Wrapping the Map
Fragment inside a custom View Group (here CustomLayout)
3.
Override the CustomLayout's
dispatchTouchEvent and (if the InfoWindow is currently shown) first route the
MotionEvents to the previously created InfoWindow. If it doesn't consume the
MotionEvents (like because you didn't click on any clickable area inside
InfoWindow etc.) then (and only then) let the events go down to the CustomLayout's
super class so it will eventually be delivered to the map.
Here is the CustomLayout Code
==============================Code==============================
package
ankit.custominfowindow;
import
android.content.Context;
import
android.graphics.Point;
import
android.util.AttributeSet;
import
android.view.MotionEvent;
import android.view.View;
import
android.widget.RelativeLayout;
import
com.google.android.gms.maps.GoogleMap;
import
com.google.android.gms.maps.model.Marker;
public class CustomLayout extends RelativeLayout {
private GoogleMap map;
private int offPXL;
private Marker marker;
private View infoWindow;
public CustomLayout(Context
context) {
super(context);
}
public CustomLayout(Context
context, AttributeSet attrs) {
super(context, attrs);
}
public CustomLayout(Context
context, AttributeSet attrs, int defStyle) {
super(context, attrs,
defStyle);
}
//Must be called before we can route the touch
events
public void init(GoogleMap map, int bottomOffsetPixels)
{
this.map = map;
this.offPXL = bottomOffsetPixels;
}
public void
setMarkerWithInfoWindow(Marker marker, View infoWindow) {
this.marker = marker;
this.infoWindow = infoWindow;
}
@Override
public boolean
dispatchTouchEvent(MotionEvent ev) {
boolean ret = false;
if (marker != null && marker.isInfoWindowShown()
&& map != null
&&
infoWindow != null) {
Point
point = map.getProjection().toScreenLocation(
marker.getPosition());
MotionEvent
copyEv = MotionEvent.obtain(ev);
copyEv.offsetLocation(-point.x + (infoWindow.getWidth() / 2),
-point.y + infoWindow.getHeight() + offPXL);
ret
= infoWindow.dispatchTouchEvent(copyEv);
}
return ret || super.dispatchTouchEvent(ev);
}
}
Second part: The next problem is that the UI changes of your
InfoWindow are not visible on screen.
To achieve that you need to manually call Marker.showInfoWindow.
Now, if you perform some permanent changes in your InfoWindow , it is good enough.
When the button is pressed, a “p” image will be visible in place of the button to show
that button has been pressed.
And Toast
will be visible to show the normal button's pressed state.
Anyway, I
wrote myself a custom class which handles the buttons state changes and all the
other things I mentioned, so here is the code:
==============================Code==============================
package
ankit.custominfowindow;
import
android.annotation.SuppressLint;
import
android.graphics.drawable.Drawable;
import android.os.Handler;
import
android.view.MotionEvent;
import android.view.View;
import
android.view.View.OnTouchListener;
import com.google.android.gms.maps.model.Marker;
public abstract class
OnInfoWindowElemTouchListener implements OnTouchListener {
private final View view;
private final Drawable bgDrawableNormal;
private final Drawable bgDrawablePressed;
private final Handler handler = new Handler();
private Marker marker;
private boolean pressed = false;
public
OnInfoWindowElemTouchListener(View view, Drawable bgDrawableNormal,
Drawable
bgDrawablePressed) {
this.view = view;
this.bgDrawableNormal = bgDrawableNormal;
this.bgDrawablePressed = bgDrawablePressed;
}
public void setMarker(Marker
marker) {
this.marker = marker;
}
@Override
public boolean onTouch(View vv,
MotionEvent event) {
if (0 <=
event.getX() && event.getX() <= view.getWidth()
&&
0 <= event.getY() && event.getY() <= view.getHeight()) {
switch
(event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
startPress();
break;
case MotionEvent.ACTION_UP:
handler.postDelayed(confirmClickRunnable, 150);
break;
case MotionEvent.ACTION_CANCEL:
endPress();
break;
default:
break;
}
}
else {
endPress();
}
return false;
}
@SuppressLint("NewApi") private void startPress() {
if (!pressed) {
pressed = true;
handler.removeCallbacks(confirmClickRunnable);
view.setBackground(bgDrawablePressed);
if (marker != null)
marker.showInfoWindow();
}
}
@SuppressLint("NewApi") private boolean endPress() {
if (pressed) {
this.pressed = false;
handler.removeCallbacks(confirmClickRunnable);
view.setBackground(bgDrawableNormal);
if (marker != null)
marker.showInfoWindow();
return true;
}
else
return false;
}
private final Runnable confirmClickRunnable = new Runnable() {
public void run() {
if (endPress()) {
onClickConfirmed(view, marker);
}
}
};
protected abstract void
onClickConfirmed(View v, Marker marker);
}
And xml file for this custominfowindow is here
==============================Code==============================
<?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:gravity="center_vertical" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:orientation="vertical" >
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Title"
android:textSize="18sp" />
<TextView
android:id="@+id/snippet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="snippet" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:orientation="vertical" >
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/btn2" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/btn" />
</LinearLayout>
</LinearLayout>
Now its time to write Main Activity. What we have to do in
main activity is to invoke above two codes appropriately.
So Code of Main Activity is here.
==============================Code==============================
package ankit.custominfowindow;
import
android.annotation.SuppressLint;
import
android.content.Context;
import android.os.Bundle;
import
android.support.v4.app.FragmentActivity;
import android.view.View;
import
android.view.ViewGroup;
import
android.widget.Button;
import
android.widget.TextView;
import
android.widget.Toast;
import
com.google.android.gms.maps.CameraUpdateFactory;
import
com.google.android.gms.maps.GoogleMap;
import
com.google.android.gms.maps.GoogleMap.InfoWindowAdapter;
import
com.google.android.gms.maps.MapFragment;
import
com.google.android.gms.maps.model.LatLng;
import
com.google.android.gms.maps.model.Marker;
import
com.google.android.gms.maps.model.MarkerOptions;
public class MainActivity extends FragmentActivity {
private ViewGroup infoWindow;
private TextView infoTitle;
private TextView infoSnippet;
private Button infoButton, infoButton2;
private
OnInfoWindowElemTouchListener infoButtonListener;
@SuppressLint("NewApi")
@Override
protected void onCreate(Bundle
savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final MapFragment
mapFragment = (MapFragment) getFragmentManager()
.findFragmentById(R.id.map);
final CustomLayout
mapWrapperLayout = (CustomLayout) findViewById(R.id.map_relative_layout);
final GoogleMap map =
mapFragment.getMap();
map.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(
28.611892,
77.376226), 16));
mapWrapperLayout.init(map,
getPixelsFromDp(this, 39 + 20));
this.infoWindow = (ViewGroup)
getLayoutInflater().inflate(
R.layout.custominfowindow, null);
this.infoTitle = (TextView) infoWindow.findViewById(R.id.title);
this.infoSnippet = (TextView) infoWindow.findViewById(R.id.snippet);
this.infoButton = (Button) infoWindow.findViewById(R.id.button);
this.infoButton2 = (Button) infoWindow.findViewById(R.id.button2);
// Setting custom
OnTouchListener which deals with the pressed state
// so it shows up
this.infoButtonListener = new
OnInfoWindowElemTouchListener(infoButton,
getResources().getDrawable(R.drawable.btn), getResources()
.getDrawable(R.drawable.btn)) {
@Override
protected void
onClickConfirmed(View v, Marker marker) {
Toast.makeText(MainActivity.this, "button Down
clicked!", Toast.LENGTH_LONG).show();
}
};
this.infoButton.setOnTouchListener(infoButtonListener);
this.infoButtonListener = new
OnInfoWindowElemTouchListener(
infoButton2,
getResources().getDrawable(R.drawable.btn2),
getResources().getDrawable(R.drawable.btn2)) {
@Override
protected void
onClickConfirmed(View v, Marker marker) {
Toast.makeText(MainActivity.this, "button Up
clicked!",
Toast.LENGTH_LONG).show();
}
};
this.infoButton2.setOnTouchListener(infoButtonListener);
map.setInfoWindowAdapter(new InfoWindowAdapter()
{
@Override
public View
getInfoWindow(Marker marker) {
return null;
}
@Override
public View
getInfoContents(Marker marker) {
infoTitle.setText(marker.getTitle());
infoSnippet.setText(marker.getSnippet());
infoButtonListener.setMarker(marker);
mapWrapperLayout.setMarkerWithInfoWindow(marker,
infoWindow);
return infoWindow;
}
});
// Let's add a
couple of markers
map.addMarker(new
MarkerOptions().title("22by4 Consulting pvt ltd")
.snippet("India").position(new LatLng(12.956098
, 77.636928)));
}
public static int
getPixelsFromDp(Context context, float dp) {
final float scale =
context.getResources().getDisplayMetrics().density;
return (int) (dp * scale +
0.5f);
}
}
Xml Code for main activity is
==============================Code==============================
<ankit.custominfowindow.CustomLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/map_relative_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<fragment
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.MapFragment"
/>
</ankit.custominfowindow.CustomLayout>
Its done here, Thanks for looking this blog.
Casino Nightclub Review - Dr.MCD
ReplyDeleteI had a great time playing the game 공주 출장샵 at this one and noticed the 안동 출장안마 amount of play I could get at it. It 익산 출장마사지 was 제주도 출장샵 a lot more fun to do the casino 서울특별 출장안마 night