codepath / android_guides

Extensive Open-Source Guides for Android Developers
guides.codepath.com
MIT License
28.3k stars 6.35k forks source link

Where should one populate / render the fragment view #76

Closed AlGantori closed 9 years ago

AlGantori commented 9 years ago

In your article "Creating and Using Fragments" I believe there should be a section eg. "Filling/Drawing the Fragment" to make it clear where I should populate the content of the fragment, is it in the OnCreateView() after inflate and just prior to "return view ;"

"view" seems also to be a member of the whole class so it could be populated from any other member like onResume()

(2) I was surprised to not see any topics in the home section about "Activities" a fundamental Android topic yet there is a section about "Fragments". https://github.com/codepath/android_guides/wiki

(3) My issue is actually with using Fragments with PagerView where I am loading ~20-40 images from the device to be paged thru and running out of memory. I've tried using the FragmentStatePageAdapter for my adapter (even though I don't believe my "images/slides" have state but without any better result. Perhaps is my bitmap sampling helper, I don't know. I wanted to ensure I am using the PagerView / Adapter / Fragments correctly and the concept still tortures me :)

Thank for trying to address the issue with sites like StackOverflow and endless goose chases and otherwise simple concepts lacking proper concise documentation and straight to the point code examples. (I posted an issue here because I did not see a way to attach it directly to your article, perhaps this is limitation of GitHub not designed to support this kind of crowd source documentation).

rogerhu commented 9 years ago

1) Yes. I'm not sure why you should be doing view inflation on onResume(). Inflation should only be happening on OnCreateView().

2) Yeah for sure the guides could use an Activity page -- there is a New Page if you might be interested to help with it! The guides are only as good as the community supporting it. :)

image

3) How big are these images? Do you need to downscale them?

LLin233 commented 9 years ago

I believe there are only fair memory you could use in an activity, if your app will only contain 20-40 images, you should consider downscale those images. yet if you app will contain more images or you want those images show with good quality, have you considered LRU cache?

Here's an answer might help you. http://stackoverflow.com/questions/1949066/java-lang-outofmemoryerror-bitmap-size-exceeds-vm-budget-android

maiserjose commented 9 years ago

Yes.

Enviado do meu Samsung Mobile da Claro

-------- Mensagem original -------- De : Leonard L notifications@github.com Data: 19/05/2015 04:44 (GMT-03:00) P/: codepath/android_guides android_guides@noreply.github.com Assunto: Re: [android_guides] Where should one populate / render the fragment view (#76)

I believe there is only fair memory you could use in an activity, if your app will only contain 20-40 images, you should consider downscale those images. yet if you app will contain more images or you want those images show with good quality, have you considered LRU cache? —Reply to this email directly or view it on GitHub.

AlGantori commented 9 years ago

My images are 1600x1200 in sets of 20-40 each and which I download and store on the device each paired with an .mp3 to implement like a "talking slide-show" to teach a particular topic. My download routine does not currently query my server to download only a size at most matching the device screen-size. However, for loading into an ImageView in fragment of the PageViewer, I am using a down-sampling code I put together overtime from the net and which I still question (my skills are getting challenged in Android needing careful garbage collection) I will paste below. Today, I think from you docs I added ViewPager.OffscreenPageLimit = 2; right after ViewPager.Adapter = new PagerAdapter( PagerItems, this.FragmentManager); and I did not see the out-of-memory while trying a new emulator from Genymotion, I typically run directly on my phone. I also don't see the issue on my daughter's Android tablet. However my Galaxy S5 running 5.0 (says up to date) will fail either trying to playback all the picture/sound slides or while switching from one set to another (ie re-entering the slides player activity with a different set). My GraphicsHelper.Load() will debug toast me with "GH: ran out of memory" because the Bitmap returned from ImageSampler.DecodeSampledBitmapFromFileName(....) is null. When this occurs and I exit back the lesson selection activity it is also impacted where the lesson thumbnails (ImageViews in a LIstView) are rendered as xamarin icons (they fail to display).

Here is my GraphicsHelper.Load code...

    //----------------------------------------------------------------------
    // 20140402
    /// <summary>
    /// Load an ImageView with the specified file from local storage
    /// </summary>
    /// <param name="Activity"></param>
    /// <param name="ImageView"></param>
    /// <param name="FileName"></param>
    public static void Load(Activity Activity, ImageView ImageView, String FileName)
    {
        Log.MethodCalled("FileName= " + FileName);
        // -) GRA
        if (File.Exists(FileName))
        {
            try
            {
                // -)  
                using (Bitmap Bitmap = ImageSampler.DecodeSampledBitmapFromFileName(FileName, DefaultImageWidth, DefaultImageHeight))
                {
                    if (Bitmap == null)
                    {
                        //then there wasn't enough memory
                        Toast.MakeText(Activity, "GH: Ran out of memory", ToastLength.Short).Show();
                        System.Console.WriteLine("GH: Ran out of memory");
                        return;
                    }
                    ImageView.SetImageBitmap(Bitmap);
                    //Bitmap.Recycle();  // causes app to close
                    Bitmap.Dispose();                       
                }

                //ImageSampler2.DecodeSampledBitmapFromFileName(FileName, DefaultImageWidth, DefaultImageHeight, ImageView);
            }
            catch (Exception)
            {
                throw;
            }
        }
        else
        {
            // Approach 1) Resource.Drawable.MediaNotAvailable  // not reacheable when this file is used in different project/namespace
            // Approach 2) the following approach will not get compiler caught if Identifier changes :(
            // int ResId = Activity.Resources.GetIdentifier("MediaNotAvailable", "drawable", Activity.PackageName);
            // Approach 3) expect MediaNotAvailable set via a public member.
            //
            // -) Load the MEDIA_NOT_AVAILABLE bitmap
            try
            {
                using (Bitmap Bitmap = ImageSampler.DecodeSampledBitmapFromResource(Activity.Resources, DefaultResID, DefaultImageWidth, DefaultImageHeight)) // 800, 600
                {
                    if (Bitmap == null)
                    {
                        //then there wasn't enough memory
                        Toast.MakeText(Activity, "GH: Ran out of memory", ToastLength.Short).Show();
                        System.Console.WriteLine("GH: Ran out of memory");
                        return;
                    }
                    ImageView.SetImageBitmap(Bitmap);
                    //Bitmap.Recycle();
                    Bitmap.Dispose();
                    // https://forums.xamarin.com/discussion/7288/complex-image-gallery-viewpager-fragments-and-dinamic-layouts-out-of-memory
                    // You are not disposing your Bitmaps after you have passed them to the ImageView instances, hence C# will hold a reference and the underlying Java instance will never get recycled.
                }

                //ImageSampler2.DecodeSampledBitmapFromResource(Activity.Resources, DefaultResID, DefaultImageWidth, DefaultImageHeight, ImageView);
            }
            catch (Exception)
            {
                throw;
            }

        }
        System.GC.Collect();
    }

Here is the ImageSampler.DecodeSampledBitmapFromFileName()

    // https://github.com/xamarin/monodroid-samples/blob/master/CameraAppDemo/BitmapHelpers.cs
    // 20130421
    /// <summary>
    /// Decode bitmap from local file
    /// </summary>
    /// <param name="FileName"></param>
    /// <param name="reqWidth"></param>
    /// <param name="reqHeight"></param>
    /// <returns></returns>
    public static Bitmap DecodeSampledBitmapFromFileName(String FileName, int reqWidth, int reqHeight)
    {
        // http://stackoverflow.com/questions/7535503/mono-for-android-exit-code-255-on-bitmapfactory-decodestream
        try
        {
            // First decode with inJustDecodeBounds=true to check dimensions
            var options = new BitmapFactory.Options { InPurgeable = true, InJustDecodeBounds = true };
            BitmapFactory.DecodeFile(FileName, options);  // returns BM = null 

            var options2 = new BitmapFactory.Options { }; 
            // Calculate inSampleSize
            options2.InSampleSize = CalculateInSampleSize(options, reqWidth, reqHeight);

            // Decode bitmap with inSampleSize set
            options.InJustDecodeBounds = false;
            return BitmapFactory.DecodeFile(FileName, options2);
        }
        catch (Exception e)
        {
            Android.Util.Log.Error("DecodeSampledBitmapFromFileName", "Exception: " + e.Message);
            return null;
        }

    }

I tried coding with suggested using { .. } construct but I have no idea where memory is exactly leaking on the Galaxy S5

Because I could not find a close to real-life example of a PagerView code loading and paginating bitmaps I thought recently about adapting the PagerView sample at https://github.com/Martynnw/AndroidDemos with my graphic loader/Sampler code to see if the later is indeed the culprit.. Sorry for pasting the code above as is, I don't know if there are code-formatting escapes supported here. Thank you for all the help.

maiserjose commented 9 years ago

Thank you. Ready to test this implementation. Em 19/05/2015 05:39, "AlGantori" notifications@github.com escreveu:

My images are 1600x1200 in sets of 20-40 each and which I download and store on the device each paired with an .mp3 to implement like a "talking slide-show" to teach a particular topic. My download routine does not currently query my server to download only a size at most matching the device screen-size. However, for loading into an ImageView in fragment of the PageViewer, I am using a down-sampling code I put together overtime from the net and which I still question (my skills are getting challenged in Android needing careful garbage collection) I will paste below. Today, I think from you docs I added ViewPager.OffscreenPageLimit = 2; right after ViewPager.Adapter = new PagerAdapter( PagerItems, this.FragmentManager); and I did not see the out-of-memory while trying a new emulator from Genymotion, I typically run directly on my phone. I also don't see the issue on my daughter's Android tablet. However my Galaxy S5 running 5.0 (says up to date) will fail either trying to playback all the picture/sound slides or while switching from one set to another (ie re-entering the slides player activity with a different set). My GraphicsHelper.Load() will debug toast me with "GH: ran out of memory" because the Bitmap returned from ImageSampler.DecodeSampledBitmapFromFileName(....) is null. When this occurs and I exit back the lesson selection activity it is also impacted where the lesson thumbnails (ImageViews in a LIstView) are rendered as xamarin icons (they fail to display).

Here is my GraphicsHelper.Load code...

//---------------------------------------------------------------------- // 20140402 ///

/// Load an ImageView with the specified file from local storage /// /// /// /// public static void Load(Activity Activity, ImageView ImageView, String FileName) { Log.MethodCalled("FileName= " + FileName); // -) GRA if (File.Exists(FileName)) { try { // -)

using (Bitmap Bitmap = ImageSampler.DecodeSampledBitmapFromFileName(FileName, DefaultImageWidth, DefaultImageHeight)) { if (Bitmap == null) { //then there wasn't enough memory Toast.MakeText(Activity, "GH: Ran out of memory", ToastLength.Short).Show(); System.Console.WriteLine("GH: Ran out of memory"); return; } ImageView.SetImageBitmap(Bitmap); //Bitmap.Recycle(); // causes app to close Bitmap.Dispose();

}

            //ImageSampler2.DecodeSampledBitmapFromFileName(FileName, DefaultImageWidth, DefaultImageHeight, ImageView);
        }
        catch (Exception)
        {
            throw;
        }
    }
    else
    {
        // Approach 1) Resource.Drawable.MediaNotAvailable  // not reacheable when this file is used in different project/namespace
        // Approach 2) the following approach will not get compiler caught if Identifier changes :(
        // int ResId = Activity.Resources.GetIdentifier("MediaNotAvailable", "drawable", Activity.PackageName);
        // Approach 3) expect MediaNotAvailable set via a public member.
        //
        // -) Load the MEDIA_NOT_AVAILABLE bitmap
        try
        {
            using (Bitmap Bitmap = ImageSampler.DecodeSampledBitmapFromResource(Activity.Resources, DefaultResID, DefaultImageWidth, DefaultImageHeight)) // 800, 600
            {
                if (Bitmap == null)
                {
                    //then there wasn't enough memory
                    Toast.MakeText(Activity, "GH: Ran out of memory", ToastLength.Short).Show();
                    System.Console.WriteLine("GH: Ran out of memory");
                    return;
                }
                ImageView.SetImageBitmap(Bitmap);
                //Bitmap.Recycle();
                Bitmap.Dispose();
                // https://forums.xamarin.com/discussion/7288/complex-image-gallery-viewpager-fragments-and-dinamic-layouts-out-of-memory
                // You are not disposing your Bitmaps after you have passed them to the ImageView instances, hence C# will hold a reference and the underlying Java instance will never get recycled.
            }

            //ImageSampler2.DecodeSampledBitmapFromResource(Activity.Resources, DefaultResID, DefaultImageWidth, DefaultImageHeight, ImageView);
        }
        catch (Exception)
        {
            throw;
        }

    }
    System.GC.Collect();
}

Here is the ImageSampler.DecodeSampledBitmapFromFileName()

// https://github.com/xamarin/monodroid-samples/blob/master/CameraAppDemo/BitmapHelpers.cs
// 20130421
/// <summary>
/// Decode bitmap from local file
/// </summary>
/// <param name="FileName"></param>
/// <param name="reqWidth"></param>
/// <param name="reqHeight"></param>
/// <returns></returns>
public static Bitmap DecodeSampledBitmapFromFileName(String FileName, int reqWidth, int reqHeight)
{
    // http://stackoverflow.com/questions/7535503/mono-for-android-exit-code-255-on-bitmapfactory-decodestream
    try
    {
        // First decode with inJustDecodeBounds=true to check dimensions
        var options = new BitmapFactory.Options { InPurgeable = true, InJustDecodeBounds = true };
        BitmapFactory.DecodeFile(FileName, options);  // returns BM = null

        var options2 = new BitmapFactory.Options { };
        // Calculate inSampleSize
        options2.InSampleSize = CalculateInSampleSize(options, reqWidth, reqHeight);

        // Decode bitmap with inSampleSize set
        options.InJustDecodeBounds = false;
        return BitmapFactory.DecodeFile(FileName, options2);
    }
    catch (Exception e)
    {
        Android.Util.Log.Error("DecodeSampledBitmapFromFileName", "Exception: " + e.Message);
        return null;
    }

}

I tried coding with suggested using { .. } construct but I have no idea where memory is exactly leaking on the Galaxy S5

Because I could not find a close to real-life example of a PagerView code loading and paginating bitmaps I thought recently about adapting the PagerView sample at https://github.com/Martynnw/AndroidDemos with my graphic loader/Sampler code to see if the later is indeed the culprit.. Sorry for pasting the code above as is, I don't know if there are code-formatting escapes supported here. Thank you for all the help.

— Reply to this email directly or view it on GitHub https://github.com/codepath/android_guides/issues/76#issuecomment-103400065 .

AlGantori commented 9 years ago

Prior to my GraphicHelper.Init() I've was explicitly calling
GraphicsHelper.SetImageSize(400, 300); prior to .Load(), and to my surprise my slides quality was acceptable (readable)

Using ViewPager.OffscreenPageLimit = 2; should only keep five images around 2 slides on each sizer of the pagination, right? I am not sure can't tackle LRU cache code while I am not understanding what's going on at the present time.

Here is GraphicHelper.Init()

    // http://stackoverflow.com/questions/16503064/how-to-get-screen-size-in-android-including-virtual-buttons
    // 20150312
    /// <summary>
    /// Needed to define Display size
    /// </summary>
    /// <param name="Activity"></param>
    public static void Init(Activity Activity)
    {
        var displayMetrics = new Android.Util.DisplayMetrics();
        Display display = Activity.WindowManager.DefaultDisplay;
        display.GetMetrics(displayMetrics);
        // Define default Image size for 4:3
        DefaultImageWidth = displayMetrics.WidthPixels / 2;
        DefaultImageHeight = 3 * DefaultImageWidth / 4;
        //
        DisplayHeigth = displayMetrics.HeightPixels;
        #if DEBUG
        //Toast.MakeText(Activity, "Display.Width=" + displayMetrics.WidthPixels.ToString(), ToastLength.Short).Show();
        #endif
                }

Thanks again.

AlGantori commented 9 years ago

Hello, I decided to bite the bullet and install GitHub. I've published sample code towards a "Slideshow using ViewPager with FragmentPagerAdapter " It's somewhat working on Genymotion emulator and a Galaxy tablet v4.1.1, it loads the bitmaps while paging forward but not while paging backwards. It's late and I am tired :)

However it fails loading the paging activity MainActivity.cs at var intent = new Intent(this, typeof(PagerActivity));

This happens only on physical Galaxy S5 v5.0 with the following complaints. Unhandled Exception: System.ArgumentException: type Parameter name: Type is not derived from a java type.

This is not yet a reproduction of my "ran out of memory" issues but I am puzzled why it fails on S5 and not on the tablet or emulator.

The VS2013 Solution/Project is at the following URL. https://github.com/AlGantori/AndroidSamples

For bitmaps see: DATA folder contains sample bitmaps sets Copy somewhere along Android.OS.Environment.ExternalStorageDirectory.AbsolutePath / Android/data/... on your device and adjust path in code.

NOTE: support v4 and v13 .DLLs, whatever that means :), were downloaded both from the following URL http://components.xamarin.com/view/xamandroidsupportv13-18 and are referenced inside this project. Copies are already in the published bin/ folder so you should not have to get separately.

.END.

AlGantori commented 9 years ago

Ok I just got PagerFragment.newInstance() to work correctly. I was trying to use savedInstanceState to retrieve the args :( However, running the app on a Galaxy S5 still generates the exception mentioned above comment. I tried emulator "Galaxy S5 v4.4.4 API19 1080x1920" available in Genymotion (v5.0 is not available/configurable I think) but it does not exhibit the crash and works fine.

My fix has been committed here https://github.com/AlGantori/AndroidSamples Thanks for helping me out.