Xamarin Android在网格视图中绘制的位图会导致缓慢而不连贯的滚动

时间:2015-07-24 12:28:25

标签: android xamarin xamarin.android android-canvas android-gridview

我正在构建一个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;
        }
    }
}

0 个答案:

没有答案