k1komans
k1komans

Reputation: 299

What is the simplest way to use Java code in Mono for Android?

I am trying to use popup balloons over custom overlay items in Mono for Android. I found this project https://github.com/jgilfelt/android-mapviewballoons, however it's written in Java. I've started manually converting it to C#, but it's a bit over my head. I've read that you can import java files to Mono, but I'm not sure which path is easier at this point. If someone has the projected I linked to in C# that would be even better. Thanks for any help!

Upvotes: 1

Views: 530

Answers (1)

Bryan
Bryan

Reputation: 684

I've taken the liberty to rewrite the first class for you in c#, it was fairly easy if you just clean up the difference from java to c#.

Hopefully you can use this as a basis for converting the other classes to c# for what you need.

You'll need to make sure to use the layout files, drawables, and selectors that the author uses in his github example. Include those in your project and adjust them as you see fit.

using System;
using Android.Content;
using Android.GoogleMaps;
using Android.Util;
using Android.Views;
using Android.Widget;

namespace MapViewBalloons
{
    /**
     * A view representing a MapView marker information balloon.
     * 
     * @author Jeff Gilfelt
     *
     */
    public class BalloonOverlayView : FrameLayout
    {
        private LinearLayout _layout;
        private TextView _title;
        private TextView _snippet;

        /**
         * 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)
            : base(context)
        {
            SetPadding(10, 0, 10, balloonBottomOffset);

            _layout = new LimitLinearLayout(context);
            _layout.Visibility = ViewStates.Visible;

            SetupView(context, _layout);

            FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
                    LayoutParams.WrapContent, LayoutParams.WrapContent);
            layoutParams.Gravity = GravityFlags.NoGravity;

            AddView(_layout, layoutParams);
        }

        /**
         * Inflate and initialize the BalloonOverlayView UI. Override this method
         * to provide a custom view/layout for the balloon. 
         * 
         * @param context - The activity context.
         * @param parent - The root layout into which you must inflate your view.
         */
        protected void SetupView(Context context, ViewGroup parent)
        {

            LayoutInflater inflater = (LayoutInflater)context.GetSystemService(Context.LayoutInflaterService);
            View v = inflater.Inflate(Resource.Layout.balloon_overlay, parent);
            _title = (TextView)v.FindViewById(Resource.Id.balloon_item_title);
            _snippet = (TextView)v.FindViewById(Resource.Id.balloon_item_snippet);

        }

        /**
         * Sets the view data from a given overlay item.
         * 
         * @param item - The overlay item containing the relevant view data. 
         */
        public void setData(OverlayItem item)
        {
            _layout.Visibility = ViewStates.Visible;
            SetBalloonData(item, _layout);
        }

        /**
         * Sets the view data from a given overlay item. Override this method to create
         * your own data/view mappings.
         * 
         * @param item - The overlay item containing the relevant view data.
         * @param parent - The parent layout for this BalloonOverlayView.
         */
        protected void SetBalloonData(OverlayItem item, ViewGroup parent)
        {
            if (!string.IsNullOrEmpty(item.Title))
            {
                _title.Visibility = ViewStates.Visible;
                _title.Text = item.Title;
            }
            else
            {
                _title.Text = "";
                _title.Visibility = ViewStates.Gone;
            }
            if (!string.IsNullOrEmpty(item.Snippet))
            {
                _snippet.Visibility = ViewStates.Visible;
                _snippet.Text = item.Snippet;
            }
            else
            {
                _snippet.Text = "";
                _snippet.Visibility = ViewStates.Gone;
            }
        }

        private class LimitLinearLayout : LinearLayout
        {

            private static int MAX_WIDTH_DP = 280;
            private readonly float SCALE;

            public LimitLinearLayout(Context context)
                : base(context)
            {
                SCALE = context.Resources.DisplayMetrics.Density;
            }

            public LimitLinearLayout(Context context, IAttributeSet attrs)
                : base(context, attrs)
            {
                SCALE = context.Resources.DisplayMetrics.Density;
            }

            protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
            {
                var mode = MeasureSpec.GetMode(widthMeasureSpec);
                int measuredWidth = MeasureSpec.GetSize(widthMeasureSpec);
                int adjustedMaxWidth = (int)(MAX_WIDTH_DP * SCALE + 0.5f);
                int adjustedWidth = Math.Min(measuredWidth, adjustedMaxWidth);
                int adjustedWidthMeasureSpec = MeasureSpec.MakeMeasureSpec(adjustedWidth, mode);
                base.OnMeasure(adjustedWidthMeasureSpec, heightMeasureSpec);
            }
        }
    }
}

Update: Added the other class, BalloonItemizedOverlay. I have not tested this code, it merely compiles.

using System.Collections.Generic;
using System.Linq;
using Android.GoogleMaps;
using Android.Graphics.Drawables;
using Android.OS;
using Android.Views;
using Java.Lang;

namespace MapViewBalloons
{
    public abstract class BalloonItemizedOverlay : ItemizedOverlay, View.IOnTouchListener, View.IOnClickListener
    {

        private static long BALLOON_INFLATION_TIME = 300;
        private static Handler _handler = new Handler();

        private MapView _mapView;
        private BalloonOverlayView _balloonView;
        private View _clickRegion;
        private View _closeRegion;
        private int _viewOffset;
        private MapController _mc;
        private OverlayItem _currentFocusedItem;
        private int _currentFocusedIndex;

        private float _startX,
            _startY;

        private bool _showClose = true;
        private bool _showDisclosure = false;
        private bool _snapToCenter = true;

        private static bool _isInflating = false;
        private BalloonRunnable _balloonRunnable;

        /**
         * 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)
            : base(defaultMarker)
        {
            this._mapView = mapView;
            _viewOffset = 0;
            _mc = mapView.Controller;

            _balloonRunnable = new BalloonRunnable(this);
        }

        /**
         * 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 int BalloonBottomOffset
        {
            get { return _viewOffset; }
            set { _viewOffset = value; }
        }

        /**
         * 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.
         * @param item - The item whose balloon is tapped.
         * @return true if you handled the tap, otherwise false.
         */
        protected bool OnBalloonTap(int index, OverlayItem item)
        {
            return false;
        }

        /**
         * Override this method to perform actions upon an item being tapped before 
         * its balloon is displayed.
         * 
         * @param index - The index of the item tapped.
         */
        protected void OnBalloonOpen(int index) { }

        protected override bool OnTap(int index)
        {

            _handler.RemoveCallbacks(_balloonRunnable);
            _isInflating = true;
            _handler.PostDelayed(_balloonRunnable, BALLOON_INFLATION_TIME);

            _currentFocusedIndex = index;
            _currentFocusedItem = CreateItem(index) as OverlayItem;
            SetLastFocusedIndex(index);

            OnBalloonOpen(index);
            CreateAndDisplayBalloonOverlay();

            if (_snapToCenter)
            {
                animateTo(index, _currentFocusedItem.Point);
            }

            return true;
        }

        /**
         * Animates to the given center point. Override to customize how the
         * MapView is animated to the given center point
         *
         * @param index The index of the item to center
         * @param center The center point of the item
         */
        protected void animateTo(int index, GeoPoint center)
        {
            _mc.AnimateTo(center);
        }

        /**
         * Creates the balloon view. Override to create a sub-classed view that
         * can populate additional sub-views.
         */
        protected BalloonOverlayView CreateBalloonOverlayView()
        {
            return new BalloonOverlayView(GetMapView().Context, BalloonBottomOffset);
        }

        /**
         * Expose map view to subclasses.
         * Helps with creation of balloon views. 
         */
        protected MapView GetMapView()
        {
            return _mapView;
        }

        /**
         * Makes the balloon the topmost item by calling View.bringToFront().
         */
        public void BringBalloonToFront()
        {
            if (_balloonView != null)
            {
                _balloonView.BringToFront();
            }
        }

        /**
         * Sets the visibility of this overlay's balloon view to GONE and unfocus the item. 
         */
        public void HideBalloon()
        {
            if (_balloonView != null)
            {
                _balloonView.Visibility = ViewStates.Gone;
            }
            _currentFocusedItem = null;
        }

        /**
         * 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)
        {

            foreach (var overlay in overlays)
            {
                if (overlay is BalloonItemizedOverlay && overlay != this)
                {
                    ((BalloonItemizedOverlay)overlay).HideBalloon();
                }
            }

        }

        public void HideAllBalloons()
        {
            if (!_isInflating)
            {
                List<Overlay> mapOverlays = _mapView.Overlays.ToList();
                if (mapOverlays.Count > 1)
                {
                    HideOtherBalloons(mapOverlays);
                }
                HideBalloon();
            }
        }

        public override Java.Lang.Object Focus
        {
            get
            {
                return _currentFocusedItem;
            }

            set
            {
                _currentFocusedIndex = LastFocusedIndex;
                _currentFocusedItem = (OverlayItem)value;
                if (_currentFocusedItem == null)
                {
                    HideBalloon();
                }
                else
                {
                    CreateAndDisplayBalloonOverlay();
                }
            }
        }

        /**
         * Creates and displays the balloon overlay by recycling the current 
         * balloon or by inflating it from xml. 
         * @return true if the balloon was recycled false otherwise 
         */
        private bool CreateAndDisplayBalloonOverlay()
        {
            bool isRecycled;
            if (_balloonView == null)
            {
                _balloonView = CreateBalloonOverlayView();
                _clickRegion = (View)_balloonView.FindViewById(Resource.Id.balloon_inner_layout);
                _clickRegion.SetOnTouchListener(this);
                _closeRegion = (View)_balloonView.FindViewById(Resource.Id.balloon_close);
                if (_closeRegion != null)
                {
                    if (!_showClose)
                    {
                        _closeRegion.Visibility = ViewStates.Gone;
                    }
                    else
                    {
                        _closeRegion.SetOnClickListener(this);
                    }
                }
                if (_showDisclosure && !_showClose)
                {
                    View v = _balloonView.FindViewById(Resource.Id.balloon_disclosure);
                    if (v != null)
                    {
                        v.Visibility = ViewStates.Visible;
                    }
                }
                isRecycled = false;
            }
            else
            {
                isRecycled = true;
            }

            _balloonView.Visibility = ViewStates.Gone;

            List<Overlay> mapOverlays = _mapView.Overlays.ToList();
            if (mapOverlays.Count > 1)
            {
                HideOtherBalloons(mapOverlays);
            }

            if (_currentFocusedItem != null)
                _balloonView.SetData(_currentFocusedItem);

            GeoPoint point = _currentFocusedItem.Point;
            MapView.LayoutParams layoutParams = new MapView.LayoutParams(
                    MapView.LayoutParams.WrapContent, MapView.LayoutParams.WrapContent, point,
                    MapView.LayoutParams.BottomCenter);
            layoutParams.Mode = MapView.LayoutParams.ModeMap;

            _balloonView.Visibility = ViewStates.Visible;

            if (isRecycled)
            {
                _balloonView.LayoutParameters = layoutParams;
            }
            else
            {
                _mapView.AddView(_balloonView, layoutParams);
            }

            return isRecycled;
        }

        public void SetShowClose(bool showClose)
        {
            _showClose = showClose;
        }

        public void SetShowDisclosure(bool showDisclosure)
        {
            _showDisclosure = showDisclosure;
        }

        public void SetSnapToCenter(bool snapToCenter)
        {
            _snapToCenter = snapToCenter;
        }

        public bool IsInflating
        {
            get { return _isInflating; }
            set { _isInflating = value; }
        }

        private class BalloonRunnable : Java.Lang.Object, IRunnable
        {
            private BalloonItemizedOverlay _item;
            public BalloonRunnable(BalloonItemizedOverlay item)
            {
                _item = item;
            }

            public void Run()
            {
                _item.IsInflating = false;
            }
        }

        public bool OnTouch(View v, MotionEvent e)
        {
            View l = ((View)v.Parent).FindViewById(Resource.Id.balloon_main_layout);
            Drawable d = l.Background;

            if (e.Action == MotionEventActions.Down)
            {
                if (d != null)
                {
                    int[] states = { Android.Resource.Attribute.StatePressed };
                    if (d.SetState(states))
                    {
                        d.InvalidateSelf();
                    }
                }
                _startX = e.GetX();
                _startY = e.GetY();
                return true;
            }
            else if (e.Action == MotionEventActions.Up)
            {
                if (d != null)
                {
                    int[] newStates = { };
                    if (d.SetState(newStates))
                    {
                        d.InvalidateSelf();
                    }
                }
                if (Java.Lang.Math.Abs(_startX - e.GetX()) < 40 && Java.Lang.Math.Abs(_startY - e.GetY()) < 40)
                {
                    // call overridden method
                    OnBalloonTap(_currentFocusedIndex, _currentFocusedItem);
                }
                return true;
            }
            else
            {
                return false;
            }
        }

        public void OnClick(View v)
        {
            HideBalloon();
        }
    }
}

Upvotes: 3

Related Questions