Showing posts with label Xamarin. Show all posts
Showing posts with label Xamarin. Show all posts

Sunday, July 13, 2014

Android Drawable Cache (Xamarin / Mono)

The code below is useful if you need to cache images in Android. Weak references are used to help protect against memory leaks.

Drawable Cache Class:
public class DrawableCache
{
 public Context Context { get; set; }
 Dictionary<object, WeakReference<Drawable.ConstantState>> _cache = new Dictionary<object, WeakReference<Drawable.ConstantState>>();

 public DrawableCache(Context context)
 {
  Context = context;
 }

 public void Put(object key, Drawable drawable)
 {
  _cache[key] = new WeakReference<Drawable.ConstantState>(drawable.GetConstantState(), false);
 }

 public Drawable Get(object key)
 {
  WeakReference<Drawable.ConstantState> value;
  if (_cache.TryGetValue(key, out value))
  {
   Drawable.ConstantState target;
   if (value.TryGetTarget(out target))
   {
    return target.NewDrawable(Context.Resources);
   }
   _cache.Remove(key);
  }
  return null;
 }
}
Optional Drawable Cache Key class
public class DrawableCacheKey
{
 public int Key { get; set; }
 public int Flags { get; set; }

 public DrawableCacheKey(int key, int flags)
 {
  Key = key;
  Flags = flags;
 }

 public override int GetHashCode()
 {
  int hash = 17;
  hash = hash * 23 + Key.GetHashCode();
  hash = hash * 23 + Flags.GetHashCode();
  return hash;
 }

 public override bool Equals(object obj)
 {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  var cast = obj as DrawableCacheKey;
  return cast != null && Key == cast.Key && Flags == cast.Flags;
 }
}

Wednesday, July 11, 2012

ActionBarSherlock with Mono for Android

3/7/2013 - I updated the blog post so everything should be current and relevant as of today. This was long overdue, sorry.

For instant gratification try downloading the Xamarin sample project. However this will most likely yield an outdated version of the ActionBarSherlock library.

Today I will explain how to bind the ActionBarSherlock Java library so that it can be used with Mono for Android. ActionBar compatibility is important since over 90% of Android users are still running a pre 3.0 platform. It's a luxury Java programmers have enjoyed for awhile now, which can finally be shared with fellow C# programmers.

The required steps are listed below:

Build the ActionBarSherlock Zip

  1. First, if necessary, download and install the latest version of Eclipse along with the ADT Plugin.
  2. Next, download the latest version of ActionBarSherlock. After the download finishes extract the library folder from archive.
  3. Rename the library folder to actionbarsherlock. In this tutorial, the library folder has been renamed/moved to c:\downloads\actionbarsherlock.
  4. Start Eclipse and create a new workspace. In this tutorial, I created a workspace located at c:\projects\actionbarsherlock.
  5. Begin creating a new Android project by clicking File -> New -> Other -> Android -> Android Project From Existing Code. Click Next.
  6. In the "Root Directory" field, browse to c:\downloads\actionbarsherlock. Check the "Copy projects into workspace" field. Click Finish.
  7. Click Project -> Clean and wait for Eclipse to finish building the project. Exit Eclipse after the build is complete. If you encounter any compile errors ensure that you are targetting Java 6+.
  8. Open Windows Explorer, and browse to c:\projects\actionbarsherlock\actionbarsherlock. Select the bin, assets, and res folders and add them to a new Zip file. Name the file actionbarsherlock.zip.
  9. Now browse to c:\projects\actionbarsherlock\actionbarsherlock\libs and locate the file android-support-v4.jar. No action is required yet, but remember this file location for future use.
  10. Later, both the actionbarsherlock.zip and android-support-v4.jar files will be copied into Visual Studio.

Create the Binding Library

  1. Open Visual Studio and create a new JAR Bindings Library project (click File -> New -> Project -> Mono for Android -> Java Bindings Library). Name the project ActionBarSherlockBinding and click OK.
  2. Click Project -> ActionBarSherlockBinding Properties and change the Target API level to 14+.
  3. Add a reference to the Mono.Android.Support.v4 binding library by clicking Project -> Add Reference and switching to the .NET tab. Select the Mono.Android.Support.v4 library and click OK.
  4. Now copy the actionbarsherlock.zip and android-support-v4.jar files (from the previous section) into the Jars folder of the ActionBarSherlockBinding project in Visual Studio.
  5. In the properties pane, right click on the the actionbarsherlock.zip file and change the Build Action to "LibraryProjectZip". Next, right click on the android-support-v4.jar file and change the Build Action to "ReferenceJar".
  6. Open the Metadata.xml file located under the the Transforms folder and replace all of its contents with the XML code below. Save and close the file when finished.
    <metadata>
    
      <!-- Don't bind internal packages/classes -->
      <remove-node path="/api/package[starts-with(@name, 'com.actionbarsherlock.internal')]" />
      
      <!-- Normalize the API, forced to use underscore to avoid name conflicts -->
      <attr path="/api/package[@name='android.support.v4.app']" name="managedName">Android.Support.V4.App</attr>
      <attr path="/api/package[@name='com.actionbarsherlock']" name="managedName">ActionBar_Sherlock</attr>
      <attr path="/api/package[@name='com.actionbarsherlock.app']" name="managedName">ActionBar_Sherlock.App</attr>
      <attr path="/api/package[@name='com.actionbarsherlock.view']" name="managedName">ActionBar_Sherlock.View</attr>
      <attr path="/api/package[@name='com.actionbarsherlock.widget']" name="managedName">ActionBar_Sherlock.Widget</attr>
    
    </metadata>
    
    Note: Additional documentation regarding this step can be found here. Lines 6-11 are trivial but line 4 is important.

  7. Build the solution and verify that there are no errors. There will probably be some warnings but it should still build successfully.

Reference the Binding Library

  1. In Visual Studio, add a new Android Application project to the existing solution (click File -> Add -> New Project -> Mono for Android -> Android Application). Name the project ActionBarSherlockDemo and click OK.
  2. Click Project -> ActionBarSherlockDemo Properties and change the Target API level to 14+.
  3. Add a reference to the binding library by clicking Project -> Add Reference and switching to the Projects tab. Select the ActionBarSherlockBinding project and click OK.
  4. Also add a reference to the Mono.Android.Support.v4 binding library from the .NET tab of the Add Reference dialog.
  5. Rebuild the solution and verify that there are no errors.

Extend the SherlockActivity Class

  1. Open Activity1.cs and change it so that it inherits from the ActionBar_Sherlock.App.SherlockActivity class. Documentation for the ActionBarSherlock library is more or less equivalent to the native ActionBar API.
  2. Items can be added to the ActionBar by creating a menu resource and overriding the OnCreateOptionsMenu method.
  3. Generate the AndroidManifest.xml and then edit it and set the SDK and theme like this:
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="internalOnly">
      <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="14" />
      <application android:theme="@style/Theme.Sherlock" />
    </manifest>
    
  4. Rebuild the solution, deploy to the emulator or device, and congratulations, you're finished!



By Request - An ActionBar Tab Example

This is a quick and dirty example of ActionBar tabs with Mono for Android using ActionBarSherlock. This code is only intended as a demonstration of basic concepts. Please refer to the official Android ActionBar documentation for best practices and for a more complete example.

using System;

using Android.OS;
using Android.Views;
using ActivityAttribute = Android.App.ActivityAttribute;
using ActionBarNavigationMode = Android.App.ActionBarNavigationMode;
using Android.Support.V4.App;
using ActionBar_Sherlock.App;
using Tab = ActionBar_Sherlock.App.ActionBar.Tab;

namespace ActionBarSherlockDemo
{
    [Activity(Label = "Tab Activity", MainLauncher = true)]
    public class TabActivity : SherlockFragmentActivity, ActionBar_Sherlock.App.ActionBar.ITabListener
    {
        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);
            SetContentView(Resource.Layout.Main);

            SupportActionBar.NavigationMode = (int)ActionBarNavigationMode.Tabs;
            SupportActionBar.SetDisplayShowTitleEnabled(true);

            Tab tab = SupportActionBar.NewTab();
            tab.SetTag("TAB1");
            tab.SetText("TAB 1");
            tab.SetTabListener(this);
            SupportActionBar.AddTab(tab);
            
            tab = SupportActionBar.NewTab();
            tab.SetTag("TAB2");
            tab.SetText("TAB 2");
            tab.SetTabListener(this);
            SupportActionBar.AddTab(tab);
        }

        public void OnTabSelected(Tab tab, Android.Support.V4.App.FragmentTransaction ft)
        {
            string tag = tab.Tag.ToString();

            Fragment f = SupportFragmentManager.FindFragmentByTag(tag);
            if (f != null)
            {
                ft.Show(f);
                return;
            }

            if (tag == "TAB1")
                f = new TestFragment1();
            else if (tag == "TAB2")
                f = new TestFragment2();

            ft.Add(Resource.Id.fragmentPlaceholder, f, tag);
        }


        public void OnTabUnselected(Tab tab, Android.Support.V4.App.FragmentTransaction ft)
        {
            string tag = tab.Tag.ToString();

            Fragment f = SupportFragmentManager.FindFragmentByTag(tag);
            if (f != null)
            {
                ft.Hide(f);
                return;
            }
        }

        public void OnTabReselected(Tab tab, Android.Support.V4.App.FragmentTransaction ft)
        {
            //Do nothing
        }
    }

    public class TestFragment1 : Fragment
    {
        public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle)
        {
            return inflater.Inflate(Resource.Layout.test1, container, false);
        }
    }

    public class TestFragment2 : Fragment
    {
        public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle)
        {
            return inflater.Inflate(Resource.Layout.test2, container, false);
        }
    }
}

Tuesday, February 21, 2012

Mono for Android Pros & Cons

Updated 5/4/2013

Recently, I had the opportunity to develop a project using Mono for Android. For those of you who are unfamiliar with Mono for Android, it enables you to develop Android apps using a .NET language like C# instead of Java. Overall, I've had a good experience using Mono for Android and I'm happy with the results. I've created a handful of apps using Mono for Android and they've all been successful.

The Mono for Android API closely mirrors the standard Android API. For example, nearly all of the same classes, methods, and fields are present in both APIs. One of the main differences is that Mono for Android utilizes properties and delegates which are non-existent in Java. I liked how closely the two APIs were related since this made it easy to switch back and forth between them and to use the wealth of documentation that already exists for the standard Android API.

Below is a list of pros and cons regarding Mono for Android:

PROS

  1. Share Code Between Platforms - If used in conjunction with the Mono Touch API and the Windows Phone API you can potentially reuse your business logic code for both the iPhone and Windows Phone platforms. This is possible since everything can be coded using a single, common programming language instead of 3 different programming languages. Please note that this only applies to business logic code though; presentation specific code will be different for each platform because of differences in the APIs. In my mind the ability to reuse code is the single greatest benefit in using Mono for Android.

  2. Use Your Existing .NET Skills - If you or the majority of your team members are knowledgeable with a .NET language but not with Java then using Mono for Android may be appropriate. However, I think this advantage is somewhat diminished by the fact that C# and Java are so similar. There is probably a greater benefit in using Mono Touch since this eliminates the need to learn Objective C which poses much more of a learning curve than Java.

  3. The C# Language - In my own opinion, the C# language has several advantages over Java (comparing only the core language and not other aspects such as frameworks, community, etc). LINQ, lamdas, and delegates are some of the features I love most about C#. These features aren't irreplaceable but they save time and effort. Perhaps Java's position will improve when version 8 is finally released.

CONS

  1. 3rd Party Android Libraries - In general it's more difficult (but not impossible) to use 3rd parties libraries with Mono for Android. Since most Android libraries are written in Java they must either be rewritten in C# or invoked with JNI. Mono for Android version 4.2+ includes a Binding project that can automatically wrap Java code in a JNI wrapper. The Binding tooling automates much of an otherwise difficult process. However, there is still a learning curve and manual adjustments are often necessary.

  2. Bugs - There is a significant amount of bugs in Mono for Android. Well, perhaps significant is too strong a word, but it is definitely more stable to use Java vs. Mono for Android. Two of the most serious bugs I encountered are: DateTime.Now returns time in the wrong time-zone (UTC), and using a SSL connection with WebClient causes an exception. (I believe these bugs are fixed now). Here is a full list of outstanding bugs.

  3. Debugging - Using the Mono for Android debugger can be a frustrating experience. It sometimes causes your app to crash or skips breakpoints for unknown reasons. Usually I resort to logging and avoid the debugger altogether. Xamarin has tried to fix the debugger but the problem has persisted for over a year now.

  4. Other Limitations - A list of other limitations is available here.

Personally, I'd lean more towards using Java and the standard Android API rather than using C# and the Mono for Android API. I say this because, first, I have a strong background in Java, and, second, using Java will likely result in less complications and limitations. However the ability to share the same code between Android, iPhone, and Windows Phone is very tempting and may be a deciding factor in some cases. At my current job I am part of a team of Microsoft developers, so using Mono for Android is typically the preferred method for creating apps within that environment.

Below is the Mono for Android website:

http://xamarin.com/monoforandroid