我正在构建一个Android应用程序,使用Xamarin框架,为用户显示个人资料照片。照片可以在椭圆形中绘制,就像您在任何社交网络上看到的一样,或者根据用户所在的屏幕在矩形中绘制。我正在绘制的图像是从Web服务下载的,可以是任何大小。因此,我必须缩放照片,修复任何旋转问题,然后将它们绘制成矩形或椭圆形。
我的整个过程工作正常,但我遇到的问题是当我将几个配置文件视图加载到网格视图中时,滚动事件的性能变得非常不稳定和缓慢。我也在控制台中收到警告说已跳过30帧,而且我可能在主线程上做了很多。
为了尝试解决问题,我已将图像加载到后台任务,这也很有效。但是,实际的onDraw事件是在操作系统绘制的主线程上绘制的。
我已将问题跟踪到canvas.drawBitmap()函数调用。当我注释掉这个命令时,慢滚动问题消失了,我不再在日志中收到警告。这告诉我没有draw命令我的进程足够有效,但是当我添加draw命令时,我开始遇到性能问题。
有谁知道如何优化绘图过程以便我可以实现平滑滚动?我的代码如下。
这是我的网格视图适配器类,用于加载配置文件照片视图...
namespace AndroidApp
{
public class GridViewAdapter : ArrayAdapter<UserContact>
{
private Activity ParentActivity;
#region Interface
public interface GridViewAdapterCallback
{
void DidSelectRow(Conversation conversation);
}
#endregion
#region Initialization
private int LayoutResourceId;
private List<UserContact> Data = null;
public GridViewAdapterCallback callback;
public GridViewAdapter (Context context, int layoutResourceId, List<UserContact> data, Activity parentActivity) : base (context, layoutResourceId, data)
{
this.LayoutResourceId = layoutResourceId;
this.Data = data;
this.ParentActivity = parentActivity;
}
public void SetData (List<UserContact> data)
{
this.Data = data;
}
#endregion
#region List Delegates
public override Android.Views.View GetView (int position, Android.Views.View convertView, Android.Views.ViewGroup parent)
{
if (convertView == null)
{
LayoutInflater inflater = LayoutInflater.From (this.Context);
convertView = inflater.Inflate(this.LayoutResourceId, parent, false);
}
UserContact row = this.Data[position];
this.SetContactToGridItem (convertView, row);
return convertView;
}
#endregion
#region Setters
private void SetContactToGridItem (Android.Views.View view, UserContact contact)
{
// get view references
ProfileImageView imageView = view.FindViewById(Resource.Id.profileImageView).JavaCast<ProfileImageView>();
imageView.ProfileImageViewStyle = ProfileImageViewStyle.Square;
imageView.ResetImage ();
imageView.SetContact (contact, this.ParentActivity);
TextView textView = (TextView)view.FindViewById(Resource.Id.textView);
textView.SetText (contact.GetFullName (), TextView.BufferType.Normal);
}
#endregion
}
}
这是我的主要个人资料视图类...
namespace AndroidApp
{
public enum ProfileImageViewColor
{
Default = 0,
White = 1
};
public enum ProfileImageViewStyle
{
Default = 0,
Square = 1
};
public class ProfileImageView : ImageView
{
public ProfileImageViewColor ProfileImageViewColor { get; set; }
public ProfileImageViewStyle ProfileImageViewStyle { get; set; }
private User User { get; set; }
private UserContact Contact { get; set; }
Bitmap Bitmap;
private Activity Activity { get; set; }
public ProfileImageView (System.IntPtr intPtr, Android.Runtime.JniHandleOwnership owner) : base (intPtr, owner)
{
Initialize ();
}
public ProfileImageView (Context context) : base (context)
{
Initialize ();
}
public ProfileImageView (Context context, IAttributeSet attrs) : base (context, attrs)
{
Initialize ();
}
public ProfileImageView (Context context, IAttributeSet attrs, int defStyle) : base (context, attrs, defStyle)
{
Initialize ();
}
void Initialize ()
{
this.ProfileImageViewColor = ProfileImageViewColor.Default;
this.ProfileImageViewStyle = ProfileImageViewStyle.Default;
this.SetScaleType (ScaleType.FitCenter);
this.CropToPadding = true;
}
#region Setters
public void SetUser (User user, Activity activity)
{
this.User = user;
this.Activity = activity;
if (this.User != null)
{
byte[] imageData = this.User.GetProfilePhoto ();
if (imageData != null)
{
this.SetImageData (this.User.GetProfilePhotoPath (), imageData);
}
else
{
UserBusiness.GetUserPhoto (this.User, (Shared.Error error, User usr) => {
activity.RunOnUiThread (() => {
this.SetImageData (this.User.GetProfilePhotoPath (), this.User.GetProfilePhoto ());
});
});
}
}
}
public void SetContact (UserContact contact, Activity activity)
{
this.Contact = contact;
this.Activity = activity;
if (this.Contact != null)
{
byte[] imageData = this.Contact.GetProfilePhoto();
if (imageData != null)
{
this.SetImageData (this.Contact.GetProfilePhotoPath (), imageData);
}
else
{
UserContactPhotoDownloadManager.CreateManager ().DownloadProfilePhoto (contact, (Shared.Error error, UserContact userContact) => {
activity.RunOnUiThread (() => {
this.SetImageData (this.Contact.GetProfilePhotoPath (), this.Contact.GetProfilePhoto ());
});
});
}
}
}
public override void SetImageBitmap (Bitmap bitmap)
{
if (bitmap == null)
{
this.SetImageResource (this.GetDefaultProfileImageID ());
}
else
{
ProfileImageDrawable drawable = new ProfileImageDrawable (bitmap, this.Width, this.Height, this.ProfileImageViewStyle);
this.SetImageDrawable (drawable);
}
}
public void ResetImage ()
{
if (this.Bitmap != null)
this.Bitmap.Recycle ();
this.SetImageBitmap (null);
}
#endregion
#region Private
private void SetImageData (byte[] imageData)
{
if (this.Bitmap != null)
{
this.Bitmap.Recycle ();
this.Bitmap = null;
}
if (imageData == null)
{
this.SetImageResource (this.GetDefaultProfileImageID ());
}
else
{
this.Bitmap = BitmapFactory.DecodeByteArray (imageData, 0, imageData.Length);
ProfileImageDrawable drawable = new ProfileImageDrawable (this.Bitmap, this.Width, this.Height, this.ProfileImageViewStyle);
this.SetImageDrawable (drawable);
}
}
private void SetImageData (string filePath, byte[] imageData)
{
if (this.Bitmap != null)
{
this.Bitmap.Recycle ();
this.Bitmap = null;
}
if (imageData == null)
{
this.SetImageResource (this.GetDefaultProfileImageID ());
}
else
{
this.SetImageAsync (filePath);
}
}
private void SetImageAsync (string filePath)
{
ImageDrawTask task = new ImageDrawTask (filePath, this, (Drawable drawable) => {
this.Activity.RunOnUiThread (() => {
this.SetImageDrawable (drawable);
});
});
task.Execute ();
}
private int GetDefaultProfileImageID ()
{
if (this.ProfileImageViewColor == ProfileImageViewColor.Default)
return (int)typeof (Resource.Drawable).GetField ("profile_image_placeholder").GetValue (null);
else
return (int)typeof (Resource.Drawable).GetField ("profile_image_placeholder_white").GetValue (null);
}
#endregion
}
public class ImageDrawTask: AsyncTask {
public delegate void ImageDrawTaskCompletion (Drawable drawable);
private string FilePath;
private ProfileImageView ImageView;
private ImageDrawTaskCompletion Completion;
public ImageDrawTask (string filePath, ProfileImageView imageView, ImageDrawTaskCompletion completion)
{
this.FilePath = filePath;
this.ImageView = imageView;
this.Completion = completion;
}
protected override void OnPreExecute()
{
}
protected override Java.Lang.Object DoInBackground(params Java.Lang.Object[] @params)
{
Bitmap bitmap = ImageUtilities.FixRotation (this.FilePath);
ProfileImageDrawable drawable = new ProfileImageDrawable (bitmap, this.ImageView.Width, this.ImageView.Height, this.ImageView.ProfileImageViewStyle);
Completion (drawable);
return null;
}
protected override void OnPostExecute(Java.Lang.Object result)
{
}
}
}
最后这是我的抽奖课......
namespace AndroidApp
{
public class ProfileImageDrawable : Drawable
{
Bitmap Bitmap;
ProfileImageViewStyle Style;
int Width;
int Height;
private RectF DrawFrame;
public ProfileImageDrawable (Bitmap bmp, int width, int height, ProfileImageViewStyle style)
{
this.Bitmap = bmp;
this.Style = style;
this.DrawFrame = new RectF ();
this.Width = width;
this.Height = height;
}
public override void Draw (Canvas canvas)
{
if (this.Style == ProfileImageViewStyle.Square)
{
canvas.DrawBitmap (this.Bitmap, this.GetMatrix (this.Bitmap, this.Width, this.Height), null);
}
else
{
BitmapShader bmpShader = new BitmapShader (this.Bitmap, Shader.TileMode.Clamp, Shader.TileMode.Clamp);
bmpShader.SetLocalMatrix (this.GetMatrix (this.Bitmap, this.Width, this.Height));
Paint paint = new Paint () { AntiAlias = true, Dither = true };
paint.SetShader (bmpShader);
canvas.DrawOval (this.DrawFrame, paint);
}
}
protected override void OnBoundsChange (Rect bounds)
{
base.OnBoundsChange (bounds);
this.DrawFrame.Set (0, 0, bounds.Width (), bounds.Height ());
}
public override int IntrinsicWidth {
get {
return this.Width;
}
}
public override int IntrinsicHeight {
get {
return this.Height;
}
}
public override void SetAlpha (int alpha)
{
}
public override int Opacity {
get {
return (int)Format.Opaque;
}
}
public override void SetColorFilter (ColorFilter cf)
{
}
private Matrix GetMatrix (Bitmap bmp, int width, int height)
{
Matrix mtx = new Matrix ();
float scaleWidth = ((float) width) / bmp.Width;
float scaleHeight = ((float) height) / bmp.Height;
float newWidth = 0;
float newHeight = 0;
if (scaleWidth > scaleHeight)
{
mtx.PostScale (scaleWidth, scaleWidth);
newWidth = scaleWidth * bmp.Width;
newHeight = scaleWidth * bmp.Height;
}
else
{
mtx.PostScale (scaleHeight, scaleHeight);
newWidth = scaleHeight * bmp.Width;
newHeight = scaleHeight * bmp.Height;
}
mtx.PostTranslate ((width - newWidth) / 2, (height - newHeight) / 2);
return mtx;
}
}
}