Click here to Skip to main content
15,892,697 members
Articles / Programming Languages / C#
Tip/Trick

Xamarin Android Image Auto Slider and OOM Issue Handled

Rate me:
Please Sign up or sign in to vote.
4.71/5 (4 votes)
11 Oct 2015CPOL4 min read 26.3K   3   6
This tip helps you to understand how to implement an auto image scroller and handled OOM out of memory issue in Xamarin Android.

Introduction

This tip will help you to understand how to implement auto image slider (scrolling images) in Xamarin Android. Also it handles growing heap memory by flushing the memory in background when the images are not displayed on UI.

AIM

Background

I was creating an app which has dashboard activity where images have to be scrolling and showing the latest advertisement. After researching on Google, I couldn't manage to find any easy plugin for image scroller and I ended up writing the below code.

Using the Code

Let's get straight into the code. I created an activity which in-turn call the fragment adapter and displays images. Let me show you the whole Fragment Class Code and I'll explain the code inside the class as I go.

C#
public class TestFragment: Fragment
{
    private const string KeyContent = "TestFragment:Content";
    private string _content = "???";
    private int itemData;
    private Bitmap myBitmap;
    private ImageView ivImageView;

    public static TestFragment NewInstance()
    {
        TestFragment f = new TestFragment ();
        return f;
    }
    public void setImageList(int intData)
    {
        this.itemData = intData;
    }

    public override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        if ((savedInstanceState != null)
            && savedInstanceState.ContainsKey(KeyContent))
            _content = savedInstanceState.GetString(KeyContent);
    }

    public override View OnCreateView(LayoutInflater inflater,
                      ViewGroup container, Bundle savedInstanceState)
    {
        View root = inflater.Inflate
              (Resource.Layout.ViewImageFlipper, container, false);
        ivImageView = root.FindViewById<imageview>(Resource.Id.img);

        int height = ivImageView.Height;
        int width = Resources.DisplayMetrics.WidthPixels;

        BitmapFactory.Options options = GetBitmapOptionsOfImage();

        //option #1
        using (Bitmap bitmapToDisplay =
            LoadScaledDownBitmapForDisplay (options, 500, 300)) {
            ivImageView.SetImageBitmap(bitmapToDisplay);
        }
        return root;
    }
    public Bitmap LoadScaledDownBitmapForDisplay
              (BitmapFactory.Options options,int reqWidth, int reqHeight)
    {
        // Calculate inSampleSize
        options.InSampleSize =
                  CalculateInSampleSize(options, reqWidth, reqHeight);
        // Decode bitmap with inSampleSize set
        options.InJustDecodeBounds = false;
        return BitmapFactory.DecodeResource(Resources, itemData, options);
    }
    public BitmapFactory.Options GetBitmapOptionsOfImage()
    {
        //Get only the bounds of the bitmap image
        BitmapFactory.Options options = new BitmapFactory.Options
        {
            InJustDecodeBounds = true,
            InPurgeable=true,
        };

        // The result will be null because InJustDecodeBounds == true.
        Bitmap result=
           BitmapFactory.DecodeResource(Resources, itemData, options);
        int imageHeight = options.OutHeight;
        int imageWidth = options.OutWidth;
        Console.WriteLine(string.Format("Original Size= {0}x{1}",
                                  imageWidth, imageHeight));
        return options;
    }

    public override void OnDestroyView()
    {
        //When Image Is out of view, Clear the Memory use.
        base.OnDestroyView ();
        if (ivImageView != null) {
            ivImageView.SetImageBitmap (null);
            GC.Collect (1);
        }
    }
}
  • Fragment: A Fragment represents a behavior or a portion of user interface in an Activity. You can combine multiple fragments in a single activity to build a multi-pane UI and reuse a fragment in multiple activities. To create a Fragment, a class must inherit from Android.App.Fragment and then override the OnCreateView method.
  • OnCreateView: The system calls this when it's time for the fragment to draw its user interface for the first time. In the above implementation, the code is trying to decode the bitmap image by calling method LoadScaledDownBitmapForDisplay and assigning the image to the image view (ivImageView).
  • GetBitmapOptionsOfImage: This method lets you specify decoding options, such as loading a smaller version of the bitmap, via the BitmapFactory.Options class. Setting the InJustDecodeBounds property to true while decoding avoids memory allocation, returning null for the bitmap object but setting OutWidth, OutHeight and OutMimeType . This technique allows you to read the dimensions and type of the image data prior to construction (and memory allocation) of the bitmap.
  • OnDestroyView: Called when the view previously created by Fragment.OnCreateView(LayoutInflater,ViewGroup,ViewGroup) has been detached from the fragment. This helps in releasing the memory of the object consumed by the previous fragment. In the above implementation, the code is assigning bitmap ivImageView.SetImageBitmap (null); which helps in releasing the memory of that fragment when it is no longer displayed. Also GC.Collect(1) is used to release memory specially in lollipop devices.
    Note: You can also make use of any caching library to cache the images if you are downloading the images from the server. The above example has images in its resource folder so I'm not worried about caching images at the moment.

Now let's create an activity which then calls the fragment state pager adapter and sets the image fragments (using TestFragment class).

C#
[Activity (Label = "AutoImageScroller")]
public class AutoImageScroller : FragmentActivity
{
    Button  btn;
    private List<int> itemData;
    private int imageValue;
    FragStateSupport _adapter;
    ViewPager _pager;

    //IpageIndicator is used to show current circles on the images as indicator;
    protected IPageIndicator _indicator;
    bool Continue = false;

    protected override void OnCreate (Bundle bundle)
    {
        base.OnCreate (bundle);

        // Create your application here
        itemData = new List<int> ();
        itemData.Add (Resource.Drawable.photo1);
        itemData.Add (Resource.Drawable.photo2);
        itemData.Add (Resource.Drawable.photo3);
        itemData.Add (Resource.Drawable.photo4);
        imageValue = 0;

        SetContentView(Resource.Layout.simple_circle_viewpager);

        //Set up adapter with List of photo ID as item Data
        _adapter = new FragStateSupport(SupportFragmentManager,itemData);

        //Setup pager reference
        _pager = FindViewById<viewpager>(Resource.Id.pager);
        _pager.Adapter = _adapter;

        //Setup CirclePageIndicator Reference
        _indicator = FindViewById<circlepageindicator>(Resource.Id.indicator);
        _indicator.SetViewPager(_pager);
    }
  • FragmentActivity: The above activity class is inherited from the fragment activity class. Using FragmentActivity, you can easily build tab and swap format. For each tab, you can use different Fragment (Fragments are reusable). So for any FragmentActivity, you can reuse the same Fragment.
  • FragStateSupport: The above activity implements a FragStateSupport adapter class and sets the list of images to be displayed in the fragment. Here FragStateSupport class is inherited from the FragmentStatePagerAdapter class.
  • FragmentStatePagerAdapter: This is used to make our TestFragment class to be efficient and only create new Fragment instances when necessary. In order to tell our ViewPager which pages it should show, we have implemented a FragmentStatePagerAdapter. Each page is a fragment which gets destroyed as soon as it’s not visible to the user, i.e., the user navigates to another one. Due to this behaviour, it consumes much less memory but then doesn’t perform as well as its counterpart (FragmentPagerAdapter). Hence, it’s perfect in the cases where the number of pages is high or undetermined.
    Note: The FragStateSupport class is not shown in this article which is a very basic class implementing FragmentStatePagerAdapter. You can see the implementation of the class in the attached project.

Now, it is time to implement the logic in the activity class which runs on the background thread continuously and keep rotating/changing the image fragment dynamically for a given time interval and updates the UI.

C#
async void HandleClick (object sender, EventArgs e)
{
    ThreadPool.QueueUserWorkItem (o => RunSlowMethod ());
}

public void RunSlowMethod()
{
    while (Continue) {
        imageValue = imageValue + 1;
        if (imageValue > 3) {
            imageValue = 0;
            Console.WriteLine("Compute reset to first image : t" + imageValue);
        }
        //displaying image for 2 seconds
        Thread.Sleep (2000);
        //Updating the view pager with the next image.
        this.RunOnUiThread (() => _pager.SetCurrentItem (imageValue, true));
    }
}

The above code is pretty self explanatory, i.e., the code is running on the background thread and updating the viewpager (_pager) with the next image value. As soon as it reaches the last images, it resets the imageValue to start from first.
That's all to implement auto image scroller in the Android app.

Points of Interest

I managed to implement auto image scroller with less amount of code and OOM for bitmap images reaching the heap memory has been reduced. I realize this is a cool tip to implement simple auto image scroller. Why use FragmentStatePagerAdapter? is explained in this tip when you have undefined number of images/fragments and how to re-use them with less memory consumption.

History

  • V1.0: Initial version on October 11, 2015

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
New Zealand New Zealand
I am a professional software developer/Analyst programmer with over 4 years of commercial development experience, focusing on the design, development and testing of software solutions using Microsoft technologies. I have been learning android using Xamarin and visual studio in my free time.

Outside of work , I love playing cricket, volleyball, swimming and adventure , travelling are my hobbies.

Comments and Discussions

 
QuestionSource Code Pin
Member 153201126-Mar-19 6:56
Member 153201126-Mar-19 6:56 
QuestionCode is not working Pin
Shahida Umar6-Mar-17 20:22
Shahida Umar6-Mar-17 20:22 
QuestionFragStateSupport Pin
megalomax26-Apr-16 10:40
megalomax26-Apr-16 10:40 
QuestionCode Pin
megalomax20-Apr-16 11:57
megalomax20-Apr-16 11:57 
AnswerRe: Code Pin
khanzzirfan24-May-16 21:54
khanzzirfan24-May-16 21:54 
GeneralMy vote of 5 Pin
Farhad Reza15-Oct-15 21:02
Farhad Reza15-Oct-15 21:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.