Hello,
I would like to do a complex image gallery (swipe left-right, change image distribution by page, select one image and upload context, ...). I have been looking for a lot of information to do it. I have optimize my code with these posts:
http://forums.xamarin.com/discussion/comment/1233/#Comment_1233
http://developer.android.com/training/displaying-bitmaps/display-bitmap.html
http://developer.android.com/training/displaying-bitmaps/process-bitmap.html
http://docs.xamarin.com/recipes/android/resources/general/load_large_bitmaps_efficiently
...
but the problem continues (out of memory). Attached file (TEST PROJECT).
There is a folder with a lot of images in sdcard. I want show all in any structure.
My solution to do a complex image gallery is:
- FragmentActivity.
- 3 fragments (front fragment -> viewpager, two lateral fragments(more information by product)) in FragmentManager.
- Viewpager
- Each fragment have x subfragments. (depends images by page).
Code (Attached file)
ViewPager definition
class FragmentViewPager : Fragment { private ViewPager pViewPager;
public ViewPager ViewPager
{
get { return pViewPager; }
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle)
{
View v = inflater.Inflate(Resource.Layout.FragmentViewPager, container, false);
pViewPager = v.FindViewById<ViewPager>(Resource.FragmentViewPager.pager);
return v;
}
//LLamada desde el activity
public void Init(List<MGeneral.GrupoArticulo> lista)
{
Adaptador ad = new Adaptador(FragmentManager, lista);
pViewPager.Adapter = ad;
}
}
public class GrupoArticulo
{
private int pCodigo;
private List<string> pArticulos;
public int Filas, Columnas;
public int Codigo
{
get { return this.pCodigo; }
set { this.pCodigo = value; }
}
public List<string> Articulos
{
get { return this.pArticulos; }
set { this.pArticulos = value; }
}
}
Adapter
class Adaptador : FragmentStatePagerAdapter { private List<MGeneral.GrupoArticulo> pListaGrupoArticulos;
public Adaptador(Android.Support.V4.App.FragmentManager fm, List<MGeneral.GrupoArticulo> list)
: base(fm)
{
this.pListaGrupoArticulos = list;
}
public override Java.Lang.Object InstantiateItem(View p0, int p1)
{
return base.InstantiateItem(p0, p1);
}
//Actualizas siempre todos los elementos
public override int GetItemPosition(Java.Lang.Object p0)
{
return PositionNone;
}
public override void StartUpdate(View p0)
{
base.StartUpdate(p0);
}
public override Fragment GetItem(int position)
{
MGeneral.GrupoArticulo gf = this.pListaGrupoArticulos[position];
return Fragmento.Nuevo(gf); //<------- IMPORTANT
}
public override int Count
{
get { return this.pListaGrupoArticulos.Count; }
}
}
Fragmento definition
class Fragmento : Fragment { public const string cParametroFilas = "filas"; public const string cParametroColumnas = "columnas"; public const string cParametroArticulos = "articulos";
private int pFilas;
private int pColumnas;
private List<string> pArticulos;
private SubFragmento pSelected;
public int Filas
{
get { return this.pFilas; }
set { pFilas = value; }
}
public int Columnas
{
get { return this.pColumnas; }
set { pColumnas = value; }
}
public SubFragmento Selected
{
get { return pSelected; }
}
public static Fragmento Nuevo(MGeneral.GrupoArticulo gf) //<------- IMPORTANT
{
Fragmento fragment;
Bundle bundle;
fragment = new Fragmento();
bundle = new Bundle();
bundle.PutInt(Fragmento.cParametroFilas, gf.Filas);
bundle.PutInt(Fragmento.cParametroColumnas, gf.Columnas);
bundle.PutStringArrayList(Fragmento.cParametroArticulos, gf.Articulos.ToArray());
fragment.Arguments = bundle;
return fragment;
}
public override Android.Views.View OnCreateView(Android.Views.LayoutInflater inflater, Android.Views.ViewGroup viewGroup, Android.OS.Bundle bundle)
{
this.pFilas = this.Arguments.GetInt(cParametroFilas);
this.pColumnas = this.Arguments.GetInt(cParametroColumnas);
this.pArticulos = new List<string>(this.Arguments.GetStringArrayList(cParametroArticulos));
return CrearGrupoArticulos();
}
public override void OnActivityCreated(Bundle p0)
{
base.OnActivityCreated(p0);
}
//Create LAYOUT DINAMICALLY.
//_________________________________
public View CrearGrupoArticulos()
{
int posicion = 0;
LinearLayout tableLayout = new LinearLayout(this.Activity);
tableLayout.Orientation = Orientation.Vertical;
LinearLayout.LayoutParams parametros = new LinearLayout.LayoutParams(TableRow.LayoutParams.FillParent, TableRow.LayoutParams.FillParent, 1.0f);
tableLayout.LayoutParameters = parametros;
LinearLayout LayoutRow;
SubFragmento Farticulo;
for (int row = 0; row < this.pFilas; row++)
{
LayoutRow = new LinearLayout(this.Activity);
LayoutRow.Orientation = Orientation.Horizontal;
LayoutRow.SetBackgroundColor(Color.White);
parametros = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FillParent, LinearLayout.LayoutParams.FillParent, 1.0f);
LayoutRow.LayoutParameters = parametros;
for (int column = 0; column < this.pColumnas; column++)
{
Farticulo = new SubFragmento(this.Activity, 1, this.pArticulos[posicion]); //<------- IMPORTANT
Farticulo.OnClick += new EventHandler(Farticulo_OnClick);
LayoutRow.AddView(Farticulo.Vista);
posicion++;
}
tableLayout.AddView(LayoutRow);
}
return tableLayout;
}
}
Subfragmento definition
class SubFragmento : Fragment { private TextView txCantidad; private TextView txDescripcion; private ImageButton imArticulo;
private int pCantidad = 0;
private int pUnidadesMedida = 1;
private string pPathImagen = Android.OS.Environment.ExternalStorageDirectory.Path;
private View pView;
private bool pSeleccionado;
int FinalHeight;
int FinalWidth;
public event EventHandler OnClick;
public View Vista
{
get { return this.pView; }
}
public bool Seleccionado
{
get { return this.pSeleccionado; }
set { this.pSeleccionado = value; }
}
public int Cantidad
{
get { return this.pCantidad; }
}
public SubFragmento(Context context, int unidadesMedida, string articulo)
{
this.pUnidadesMedida = unidadesMedida;
this.pPathImagen = System.IO.Path.Combine(this.pPathImagen, "catalog/" + articulo);
Init(context);
}
/// <summary>
/// Create controls dinamically
/// </summary>
public View Init(Context context)
{
txDescripcion = new TextView(context);
RelativeLayout.LayoutParams param = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.FillParent, RelativeLayout.LayoutParams.WrapContent);
txDescripcion.SetMaxLines(2);
txDescripcion.LayoutParameters = param;
txDescripcion.SetTextColor(Color.Black);
txDescripcion.Text = "042245 Fragance 100ml.";
imArticulo = new ImageButton(context);
LinearLayout.LayoutParams params2 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FillParent, LinearLayout.LayoutParams.FillParent, 1.0f);
imArticulo.LayoutParameters = params2;
imArticulo.SetBackgroundColor(Color.White);
imArticulo.Click += new EventHandler(imArticulo_Click);
txCantidad = new TextView(context);
RelativeLayout.LayoutParams param3 = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WrapContent, RelativeLayout.LayoutParams.WrapContent);
param3.AddRule(LayoutRules.AlignParentRight);
txCantidad.SetBackgroundColor(Color.Black);
// use same id as defined when adding the button
txCantidad.LayoutParameters = param3;
txCantidad.SetTextSize(Android.Util.ComplexUnitType.Dip, 24);
RelativeLayout layout = new RelativeLayout(context);
layout.SetBackgroundColor(Color.White);
layout.LayoutParameters = new RelativeLayout.LayoutParams(LinearLayout.LayoutParams.FillParent, RelativeLayout.LayoutParams.WrapContent);
layout.AddView(imArticulo);
layout.AddView(txCantidad);
layout.AddView(txDescripcion);
LinearLayout main = new LinearLayout(context);
main.LayoutParameters = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FillParent, LinearLayout.LayoutParams.FillParent, 1.0f);
main.AddView(layout);
this.pView = main;
//Upload width / height dinamically to change fragme imagebutton
ViewTreeObserver vto = imArticulo.ViewTreeObserver;
vto.GlobalLayout += (sender, args) =>
{
if (imArticulo.Width != FinalWidth || imArticulo.Height != FinalHeight)
{
FinalWidth = imArticulo.Width;
FinalHeight = imArticulo.Height;
LoadImage(); //<------- IMPORTANT
}
};
return main;
}
private void LoadImage()
{
Bitmap bitmap;
MGeneral.pLRUCache.TryGetValue(this.pPathImagen, out bitmap);
if (bitmap != null)
{
imArticulo.SetImageBitmap(bitmap);
//bitmap = null;
}
else
{
MGeneral.BitmapWorkerTask task = new MGeneral.BitmapWorkerTask(imArticulo, 1, FinalWidth , FinalHeight);
task.Execute(this.pPathImagen);
}
}
Load image in imagebutton
public class BitmapWorkerTask : AsyncTask
{
private WeakReference imageViewReference;
private int sampleSize = 0;
private int reqHeight = 0;
private int reqWidht = 0;
private string path;
public BitmapWorkerTask(ImageButton imageView, int pSampleSize, int pReqWidth, int pReqHeight)
{
//_____________________________________________________________________
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference(imageView);
reqHeight = pReqHeight;
reqWidht = pReqWidth;
sampleSize = pSampleSize;
}
protected override Java.Lang.Object DoInBackground(params Java.Lang.Object[] @params)
{
path = @params[0].ToString();
try
{
//return DecodeSampleBitmapFromFile(path, reqWidht, reqHeight);
return DecodeSampleBitmapFromFile(path, reqWidht, reqHeight);
}
catch (System.Exception ex)
{
Log.Debug("TT", "Exception : " + ex.Message);
return null;
}
}
protected override void OnPostExecute(Java.Lang.Object result)
{
base.OnPostExecute(result);
if (IsCancelled)
{
result = null;
Log.Debug("TT", "OnPostExecute - Task Cancelled");
}
else
{
using (Bitmap bmpResult = result as Bitmap)
{
if (imageViewReference != null && bmpResult != null)
{
ImageButton view = imageViewReference.Target as ImageButton;
if (view != null)
{
view.SetImageBitmap(bmpResult); //Actualizo el imagebutton con el bitmap resultante.
if (!pLRUCache.ContainsKey(path))
pLRUCache.Add(path, bmpResult); //Añado el bitmap en la cache de imagenes.
}
}
}
}
}
public static int CalculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight)
{
//_____________________________
// Raw height and width of image
int height = options.OutHeight;
int width = options.OutWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth)
{
if (width > height)
{
inSampleSize = (int)System.Math.Round((float)height / (float)reqHeight);
}
else
{
inSampleSize = (int)System.Math.Round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
public static Bitmap DecodeSampleBitmapFromFile(string path, int reqWidth, int reqHeight)
{
try
{
//______________________________________________________________
// First decode with inJustDecodeBounds=true to check dimensions
BitmapFactory.Options options = new BitmapFactory.Options();
options.InJustDecodeBounds = true;
BitmapFactory.DecodeFile(path, options);
//BitmapFactory.DecodeStream(url.OpenConnection().InputStream, null, options);
//______________________
// Calculate inSampleSize
options.InSampleSize = CalculateInSampleSize(options, reqWidth, reqHeight);
//____________________________________
// Decode bitmap with inSampleSize set
options.InJustDecodeBounds = false;
return BitmapFactory.DecodeFile(path, options);
}
catch (System.Exception ex)
{
Log.Debug("DecodeBitmapFromFile: ", ex.Message);
return null;
}
finally
{
//
}
}
}
Thanks