質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Xamarin

Xamarin(ザマリン)は、iPhoneなどのiOSやAndroidで動作し、C# 言語を用いてアプリを開発できるクロスプラットフォーム開発環境です。Xamarin Studioと C# 言語を用いて、 iOS と Android の両方の開発を行うことができます。

Q&A

解決済

1回答

1208閲覧

Xamarin.Android ListView 画像つき 高速処理

dfrd

総合スコア14

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Xamarin

Xamarin(ザマリン)は、iPhoneなどのiOSやAndroidで動作し、C# 言語を用いてアプリを開発できるクロスプラットフォーム開発環境です。Xamarin Studioと C# 言語を用いて、 iOS と Android の両方の開発を行うことができます。

0グッド

0クリップ

投稿2018/07/06 11:06

編集2018/07/07 14:23

Xamarin.Android
ListViewで、サムネ付きのリストアイテムを表示しています。
サムネと、テキストは、URLから、取得していて、無事に表示は出来ているのですが、

スクロールをすると、重くて、カクカクしながら表示される感じになります。

何か良い方法はありませんでしょうか?

処理の順番としては次の順番になります。

(1)APIで、記事データを取得 : AsyncTaskで処理

(2)List<T>にデータを格納

(3)Adapterにリストデータをセット

(4)AdapterClass内で、
GetView関数が処理開始。

(5)GetViewメソッドで、テキスト、画像をセットする。

まだまだ、初心者で申し訳ありませんが、ご教授をお願い致します。

以下に、Adapterのソースコードを記載させて頂きます。

namespace SampleArticleMemberApp.Droid { public class ArticleListItemAdapter : BaseAdapter<ArticleListItem> { public List<ArticleListItem> items; public Activity context; /** * コンストラクタ */ public ArticleListItemAdapter(Activity context, List<ArticleListItem> items) { Debug.Log(Debug.DEBUG_LEVEL_VERBOSE, "ArticleListItemAdapter", "-----START-----"); this.context = context; this.items = items; Debug.Log(Debug.DEBUG_LEVEL_VERBOSE, "ArticleListItemAdapter", "-----END-----"); } /** * クリック処理 */ public override long GetItemId(int position) { Debug.Log(Debug.DEBUG_LEVEL_VERBOSE, "GetItemId", "-----START-----"); Debug.Log(Debug.DEBUG_LEVEL_VERBOSE, "GetItemId", "-----END-----"); return position; } public override ArticleListItem this[int position] { get { return items[position]; } } public override int Count { get { return items.Count; } } public override View GetView(int position, View convertView, ViewGroup parent) { Debug.Log(Debug.DEBUG_LEVEL_VERBOSE, "GetView", "-----START-----"); var item = items[position]; View view = convertView; if (view == null){ view = context.LayoutInflater.Inflate(Resource.Layout.ArticleListViewItem, null); } // BaseAdapter<T>の対応するプロパティを割り当て view.FindViewById<TextView>(Resource.Id.articleItemDate).Text = item.date; view.FindViewById<TextView>(Resource.Id.articleItemTitle).Text = item.title; Debug.Log(Debug.DEBUG_LEVEL_INFO, "GetView", item.title); Debug.Log(Debug.DEBUG_LEVEL_INFO, "GetView", item.url); Debug.Log(Debug.DEBUG_LEVEL_INFO, "GetView", item.thumbUrl); Debug.Log(Debug.DEBUG_LEVEL_INFO, "GetView", "readable: " +item.readable.ToString()); if(item.thumbUrl != ""){ Debug.Log(Debug.DEBUG_LEVEL_INFO, "GetView", "画像を読み込みます"); view.FindViewById<ImageView>(Resource.Id.articleItemThumb).SetImageResource(Resource.Mipmap.def_thumb); SetThumb(view, position); //var image = GetImageBitmapFromUrl(item.thumbUrl); //view.FindViewById<ImageView>(Resource.Id.articleItemThumb).SetImageBitmap(image); } else{ view.FindViewById<ImageView>(Resource.Id.articleItemThumb).SetImageResource(Resource.Mipmap.def_thumb); } //読み込み不可能ならば、背景色を変える if(!item.readable){ view.SetBackgroundColor(Color.DarkGray); view.FindViewById<TextView>(Resource.Id.articleItemDate).SetTextColor(Color.GhostWhite); view.FindViewById<TextView>(Resource.Id.articleItemDate).Alpha = 0.4f; view.FindViewById<TextView>(Resource.Id.articleItemTitle).SetTextColor(Color.GhostWhite); view.FindViewById<TextView>(Resource.Id.articleItemTitle).Alpha = 0.4f; view.FindViewById<ImageView>(Resource.Id.articleItemThumb).Alpha = 0.4f; } else{ view.SetBackgroundColor(Color.GhostWhite); view.FindViewById<TextView>(Resource.Id.articleItemDate).SetTextColor(Color.Black); view.FindViewById<TextView>(Resource.Id.articleItemDate).Alpha = 1.0f; view.FindViewById<TextView>(Resource.Id.articleItemTitle).SetTextColor(Color.Black); view.FindViewById<TextView>(Resource.Id.articleItemTitle).Alpha = 1.0f; view.FindViewById<ImageView>(Resource.Id.articleItemThumb).Alpha = 1.0f; } Debug.Log(Debug.DEBUG_LEVEL_VERBOSE, "GetView", "-----END-----"); return view; } /** * URLから、Bitmap生成 */ private async Task<Bitmap> GetImageBitmapFromUrl(string url) { Bitmap imageBitmap = null; try{ using (var webClient = new WebClient()) { var imageBytes = webClient.DownloadData(url); if (imageBytes != null && imageBytes.Length > 0) { imageBitmap = BitmapFactory.DecodeByteArray(imageBytes, 0, imageBytes.Length); } } } catch(Exception e) { Debug.Log(Debug.DEBUG_LEVEL_ERROR, "GetImageBitmapFromUrl", "e: " +e); } return imageBitmap; } private async void SetThumb(View view, int position) { var item = items[position]; if(items[position].bitmap == null) { ImageView imageView = view.FindViewById<ImageView>(Resource.Id.articleItemThumb); Debug.Log(Debug.DEBUG_LEVEL_ERROR, "GetImageBitmapFromUrl", "imgvW: " + imageView.Width); Debug.Log(Debug.DEBUG_LEVEL_ERROR, "GetImageBitmapFromUrl", "imgvH: " + imageView.Height); if(0 < imageView.Width && 0 < imageView.Height){ Bitmap bmp = await GetImageBitmapFromUrl(item.thumbUrl); int bmpH = (int)((float)imageView.Width / (float)bmp.Width *(float)bmp.Height); int scale = Math.Max(imageView.Width, bmpH); bmp = Bitmap.CreateScaledBitmap(bmp, imageView.Width, bmpH, false); items[position].bitmap = await GetImageBitmapFromUrl(item.thumbUrl); view.FindViewById<ImageView>(Resource.Id.articleItemThumb).SetImageBitmap(items[position].bitmap); Debug.Log(Debug.DEBUG_LEVEL_ERROR, "GetImageBitmapFromUrl", "bmpW: " + bmp.Width); Debug.Log(Debug.DEBUG_LEVEL_ERROR, "GetImageBitmapFromUrl", "bmpH: " + bmp.Height); bmp.Recycle(); bmp = null; } } else { view.FindViewById<ImageView>(Resource.Id.articleItemThumb).SetImageBitmap(items[position].bitmap); } } } }

以上です。
よろしくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

f-miyu

2018/07/06 23:56

実際のソースコード(特にGetViewメソッド)を載せていただけると、ここが原因だとアドバイスできるかもしれません。
dfrd

2018/07/07 14:23

全体的にクラスのソースを記載させて頂きました。お恥ずかしいソースですが、よろしくお願い致します。
guest

回答1

0

ベストアンサー

GetImageBitmapFromUrlwebClient.DownloadData(url)で画像が取得できるまで処理を止めてしまうので、カクカクした動きになってしまっています。
ここを非同期処理にすれば改善されるかと思います。

C#

1private async Task<Bitmap> GetImageBitmapFromUrl(string url) 2{ 3 Bitmap imageBitmap = null; 4 5 try 6 { 7 using (var webClient = new WebClient()) 8 { 9 var tcs = new TaskCompletionSource<Bitmap>(); 10 11 webClient.DownloadDataCompleted += (sender, e) => 12 { 13 var imageBytes = e.Result; 14 if (imageBytes != null && imageBytes.Length > 0) 15 { 16 imageBitmap = BitmapFactory.DecodeByteArray(imageBytes, 0, imageBytes.Length); 17 tcs.SetResult(imageBitmap); 18 } 19 }; 20 21 webClient.DownloadDataAsync(new Uri(url)); 22 23 imageBitmap = await tcs.Task; 24 } 25 } 26 catch (Exception e) 27 { 28 Debug.Log(Debug.DEBUG_LEVEL_ERROR, "GetImageBitmapFromUrl", "e: " +e); 29 } 30 return imageBitmap; 31}

試したところ、これで一応改善はされましたが、より効率化を目指すのであれば、画像の非同期取得やキャッシュを行うための優れたOSSライブラリがあるので、それらの使用を検討されてはいかがでしょうか?

Xamarin.Androidに対応したライブラリとしては下記のものがあります。

GlideやPicassoは、元のJavaでもよく使われているライブラリです。
例としてFFImageLoadingを使えば、こんな感じで簡単に画像の取得が出来ます。

C#

1ImageService.Instance.LoadUrl(url).Into(imageView):

追記

今のコードのままでは、少し問題が出てくるかもしれません。

  1. 画像取得に失敗(通信できないなど)したら、Taskが終わらない
  2. スクロールしなければ、画像の読み込みが行われない

1.に関しては、DownloadDataCompletedを以下のように修正すれば、大丈夫かと思います。

C#

1webClient.DownloadDataCompleted += (sender, e) => 2{ 3 Bitmap image = null; 4 try 5 { 6 if (!e.Cancelled && e.Error == null) 7 { 8 var imageBytes = e.Result; 9 if (imageBytes != null && imageBytes.Length > 0) 10 { 11 image = BitmapFactory.DecodeByteArray(imageBytes, 0, imageBytes.Length); 12 } 13 } 14 } 15 finally 16 { 17 tcs.SetResult(image); 18 } 19};

ただ、GetImageBitmapFromUrlnullを返すので、このメソッドを使っているSetThumbでは、nullチェックが必要になります。

2.に関しては、SetThumbで、imageViewのサイズチェックをして、0なら処理を行なっていないためです。この時imageViewはまだサイズが確定していないので、WidthHeightは0を返します。
よくよく見ると、リサイズ処理は行っているようですが、使ってはいないようなので、サイズチェック自体を行わないようにするのが一番簡単かと思います。

先に述べたライブラリを使えば、リサイズも簡単に行ってくれるので、できるなら使用することを強くお勧めします。

投稿2018/07/07 19:13

編集2018/07/08 04:51
f-miyu

総合スコア1625

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dfrd

2018/07/07 23:33

f-miyu さん ありがとうございます。 AsyncTaskをとりあえず入れてみれば、非同期になり、大丈夫かと思ったのですが、まだもう少し足りなかったのですね。 一旦、これでやってみようかと思います♪ やってみたあと、報告させて頂きます。 そして、そのあとに、 (まだ始めたばかりなので、)OSSライブラリなどは、使ったことがなく、ここで再度、聞くかもしれません。 その時は、お手数ごございますが、またよろしくお願い致します。
dfrd

2018/07/08 02:14

無事に、カクカクは無くなりました。 ありがとうございます。 イメージキャッシュ、ローディングなどのOSSは、一旦リリースを先にするため、あとで設定したいと思います。 もしかしたら、別スレッドで立ち上げるかもしれません。 その時は、お手数でございますが、もしよろしければ、教えてもらえますと幸いです。 よろしくお願い致します。
f-miyu

2018/07/08 04:52

少しきになることがあったので、回答に追記しました。
dfrd

2018/07/09 00:11

ありがとうございます! ここまで、丁寧に。。。 とても助かります。 まだまだ、初心者なもので。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問