各位程序员大家好。当我在 Visual Studio Xamarin 中为 Android 创建应用程序时,我发现内存有问题。在我的应用程序中,用户需要选择一个国家电话前缀。此 UI 是带有 ListView 的警报对话框。我用扩展适配器填充 ListView。
internal class CountryListViewHolderClass : Java.Lang.Object
{
internal Action viewClicked { get; set; }
internal TextView countryCodeView;
internal ImageView countryImageView;
internal ImageView countryCheckView;
public void initialize(View view)
{
view.Click += delegate
{
viewClicked();
};
}
}
public class CountryInfo
{
public string _flag { get; private set; }
public string _code { get; private set; }
public string _prefix { get; private set; }
public CountryInfo(string flag, string code, string prefix)
{
_flag = flag;
_code = code;
_prefix = prefix;
}
}
public class CountryListAdapter : BaseAdapter<string>
{
Activity _context;
string[] _countryCode;
string[] _countryPrefix;
string[] _countryFlag;
internal event Action<CountryInfo> actionCountrySelected;
public CountryListAdapter(Activity context, string[] countryCode, string[] countryFlag, string[] countryPrefix)
{
_context = context;
_countryCode = countryCode;
_countryFlag = countryFlag;
_countryPrefix = countryPrefix;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
CountryListViewHolderClass countryListViewHolderClass;
View view;
view = convertView;
Android.Graphics.Color viewBg;
if (view == null)
{
view = _context.LayoutInflater.Inflate(Resource.Layout.CountryListItem, parent, false);
countryListViewHolderClass = new CountryListViewHolderClass();
countryListViewHolderClass.countryCodeView = view.FindViewById<TextView>(Resource.Id.countryCodeText);
countryListViewHolderClass.countryImageView = view.FindViewById<ImageView>(Resource.Id.countryFlagImg);
countryListViewHolderClass.countryCheckView = view.FindViewById<ImageView>(Resource.Id.countryCheckImage);
countryListViewHolderClass.initialize(view);
view.Tag = countryListViewHolderClass;
}
else
{
countryListViewHolderClass = (CountryListViewHolderClass)view.Tag;
}
countryListViewHolderClass.countryCodeView.Text = "(+" + _countryPrefix[position] + ") " + _countryCode[position];
Stream flag = _context.Resources.Assets.Open("cflags/" + _countryFlag[position] + ".png");
countryListViewHolderClass.countryImageView.SetImageDrawable(Drawable.CreateFromStream(flag, _countryFlag[position]));
flag.Dispose();
flag.Close();
}
countryListViewHolderClass.viewClicked = () =>
{
if (actionCountrySelected != null)
{
for (int i=0; i < parent.ChildCount; i++)
{
View otherView = parent.GetChildAt(i);
ImageView checkIcon = otherView.FindViewById<ImageView>(Resource.Id.countryCheckImage);
checkIcon.Alpha = 0;
}
countryListViewHolderClass.countryCheckView.Alpha = 1;
CountryInfo result = new CountryInfo(_countryFlag[position], _countryCode[position], _countryPrefix[position]);
actionCountrySelected(result);
}
};
return view;
}
}
我在 Main Activity 中使用的这个 Adapter 来填充 ListView。如果我用这个 ListView 打开 AlertDialog 一切正常。如果我第二次打开同一个 AlertDialog,这会抛出 Java.Lang.OutOfMemoryError。这是我创建适配器并将其发送到 ListView 的代码
void BindCountryList()
{
string[] countryCodes = Resources.GetStringArray(Resource.Array.countries_names);
string[] countryPrefixes = Resources.GetStringArray(Resource.Array.countries_iso_prefixes);
string[] countryFlags = Resources.GetStringArray(Resource.Array.countries_iso_codes);
if (countryListAdapter != null)
{
countryListAdapter.actionCountrySelected -= CountrySelected;
countryListAdapter = null;
}
countryListAdapter = new CountryListAdapter(this, countryCodes, countryFlags, countryPrefixes);
countryListAdapter.actionCountrySelected += CountrySelected;
Stream flag = Resources.Assets.Open("cflags/tr.png");
countryCodeSelectButton.SetImageDrawable(Drawable.CreateFromStream(flag, "tr.png"));
flag.Dispose();
flag.Close();
phoneCountryPrefix.Text = "+90";
}
void CountrySelected(CountryInfo countryInfo)
{
phoneCountryPrefix.Text = "+"+countryInfo._prefix;
phonePrefix = countryInfo._prefix;
countrySelectAlert.Cancel();
Stream flag = Resources.Assets.Open("cflags/" + countryInfo._flag + ".png");
countryCodeSelectButton.SetImageDrawable(Drawable.CreateFromStream(flag, countryInfo._flag));
flag.Close();
string toast = string.Format("The prefix is {0}", countryInfo._code);
Toast.MakeText(this, toast, ToastLength.Long).Show();
}
我真的不明白内存泄漏在哪里。所有帮助将不胜感激。
正如Demitrian建议的那样,我使用了 IDisposable 模式。所以我以这种方式更改CountryListAdapter类的GetView函数
public override View GetView(int position, View convertView, ViewGroup parent)
{
CountryListViewHolderClass countryListViewHolderClass;
View view;
view = convertView;
if (view == null)
{
view = _context.LayoutInflater.Inflate(Resource.Layout.CountryListItem, parent, false);
countryListViewHolderClass = new CountryListViewHolderClass();
countryListViewHolderClass.countryCodeView = view.FindViewById<TextView>(Resource.Id.countryCodeText);
countryListViewHolderClass.countryImageView = view.FindViewById<ImageView>(Resource.Id.countryFlagImg);
countryListViewHolderClass.countryCheckView = view.FindViewById<ImageView>(Resource.Id.countryCheckImage);
countryListViewHolderClass.initialize(view);
view.Tag = countryListViewHolderClass;
}
else
{
countryListViewHolderClass = (CountryListViewHolderClass)view.Tag;
}
countryListViewHolderClass.countryCodeView.Text = "(+" + _countryPrefix[position] + ") " + _countryCode[position];
StreamReader reader=null;
try
{
reader = new StreamReader(_context.Resources.Assets.Open("cflags/" + _countryFlag[position] + ".png"));
countryListViewHolderClass.countryImageView.SetImageDrawable(Drawable.CreateFromStream(reader.BaseStream, _countryFlag[position]));
}
finally
{
if (reader != null)
{
reader.Dispose();
}
}
countryListViewHolderClass.viewClicked = () =>
{
if (actionCountrySelected != null)
{
for (int i=0; i < parent.ChildCount; i++)
{
View otherView = parent.GetChildAt(i);
ImageView checkIcon = otherView.FindViewById<ImageView>(Resource.Id.countryCheckImage);
checkIcon.Alpha = 0;
}
countryListViewHolderClass.countryCheckView.Alpha = 1;
CountryInfo result = new CountryInfo(_countryFlag[position], _countryCode[position], _countryPrefix[position]);
actionCountrySelected(result);
}
};
return view;
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句