官术网_书友最值得收藏!

  • Xamarin Blueprints
  • Michael Williams
  • 822字
  • 2021-07-08 11:48:20

Custom row appearance

Let's get back to the ListAdapter implementation and design our ListView row appearance. Open the Resources | Layout folder, create a new .xml file for the cell appearance, call it CustomCell.xml, and copy in the following XML code:

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="horizontal" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:weightSum="4"> 
    <LinearLayout 
        android:orientation="vertical" 
        android:layout_width="match_parent" 
        android:layout_height="match_parent" 
        android:layout_weight="1"> 
        <ImageView 
            android:id="@+id/image" 
            android:layout_width="100dp" 
            android:layout_height="100dp" 
            android:adjustViewBounds="true" /> 
    </LinearLayout> 
    <LinearLayout 
        android:orientation="vertical" 
        android:layout_width="match_parent" 
        android:layout_height="match_parent" 
        android:layout_weight="3" 
        android:weightSum="2"> 
        <TextView 
            android:id="@+id/title" 
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content" 
            android:layout_weight="1" /> 
        <TextView 
            android:id="@+id/date" 
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content" 
            android:layout_weight="1" /> 
    </LinearLayout> 
</LinearLayout> 

We are creating the same layout as the custom cell made for iOS, but in Android we will use the ImageView and TextView objects. Now that we have our custom cell, we can implement the the GetView function. The GetView function is exactly like the GetCell function in the preceding UITableSource implementation. Open up the ListAdapter.cs file and continue with the list adapter implementation:

public class ListAdapter : BaseAdapter 
    { 
        private List<GalleryItem> _items; 
        private Activity _context; 
 
        public ListAdapter(Activity context) : base() 
        { 
            _context = context; 
            _items = new List<GalleryItem>(); 
        } 
 
        public override Java.Lang.Object GetItem (int position) 
        { 
            return null; 
        } 
 
        public override long GetItemId(int position) 
        { 
            return position; 
        } 
 
        public override int Count 
        { 
            get 
            { 
                return items.Count;  
            } 
        } 
} 

We override the Count property and functions GetItemId and GetItem, to return the number of gallery items in our list. These override functions are exactly the same as the overrides in Java for any BaseAdapter inherited class. Now for the GetView function:

public override View GetView(int position, View convertView, ViewGroup parent) 
        { 
            View view = convertView; // re-use an existing view, if one is available 
 
            if (view == null) 
            { 
                // otherwise create a new one 
                view = context.LayoutInflater.Inflate(Resource.Layout.CustomCell, null); 
            } 
 
            // set image 
            var imageView = view.FindViewById<ImageView> (Resource.Id.image); 
            BitmapHelpers.CreateBitmap (imageView, _items [position].ImageData); 
 
            // set labels 
            var titleTextView = view.FindViewById<TextView> (Resource.Id.title); 
            titleTextView.Text = _items[position].Title; 
            var dateTextView = view.FindViewById<TextView> (Resource.Id.date); 
            dateTextView.Text = _items[position].Date; 
 
            return view; 
        } 
 
        private async void createBitmap(ImageView imageView, byte[] imageData) 
        { 
            try 
            { 
                if (imageData != null)  
                { 
                    var bm = await BitmapFactory.DecodeByteArrayAsync(imageData, 0, imageData.Length); 
                    if (bm != null)  
                    { 
                        imageView.SetImageBitmap(bm); 
                    } 
                } 
            } 
            catch (Exception e)  
            { 
                Console.WriteLine ("Bitmap creation failed: " + e); 
            } 
        } 

Notice in the GetView function we are using the CustomCell layout for each row; we also have a private method for creating our bitmaps from each model's byte array.

If we have a look at the current implementation, what do we notice here?

We are creating a bitmap every time the cell requires this data again for the view; is this efficient? No, we should be reusing bitmaps and memory as much as possible.

This tends to be a common issue with Android ListView.

What is the most memory efficient way to reuse bitmaps across hundreds of items in a ListView while scrolling and staying smooth as we move down the list at various speeds? How can we tackle this problem? Let's have a look at how we can approach this problem.

Firstly, we need to implement an object called ImageHandler. This will contain the logic for retrieving byte arrays from all gallery images on an Android device. Create a new file, name it ImageHandler, and start importing these namespaces:

namespace Gallery.Droid 
{ 
    using System; 
    using System.Collections.Generic; 
 
    using Android.Database; 
    using Android.Content; 
    using Android.Provider; 
 
    using Gallery.Shared; 
 
    public static class ImageHandler 
    { 
    } 
} 

This class will include a function, GetFiles, which will create gallery items based upon the items pulled from any device's gallery using the ContentResolver interface:

public static IEnumerable<GalleryItem> GetFiles(Context context) 
        { 
            ContentResolver cr = context.ContentResolver; 
 
            string[] columns = new string[]  
            { 
                MediaStore.Images.ImageColumns.Id, 
                MediaStore.Images.ImageColumns.Title, 
                MediaStore.Images.ImageColumns.Data, 
                MediaStore.Images.ImageColumns.DateAdded, 
                MediaStore.Images.ImageColumns.MimeType, 
                MediaStore.Images.ImageColumns.Size, 
            }; 
             
            var cursor = cr.Query(MediaStore.Images.Media.ExternalContentUri, columns, null, null, null); 
 
            int columnIndex = cursor.GetColumnIndex(columns[2]); 
 
            int index = 0; 
 
            // create max 100 items 
            while (cursor.MoveToNext () && index < 100)  
            { 
                index++; 
 
                var url = cursor.GetString(columnIndex); 
 
                var imageData = createCompressedImageDataFromBitmap (url); 
 
                yield return new GalleryItem ()  
                { 
                    Title = cursor.GetString(1), 
                    Date = cursor.GetString(3), 
                    ImageData = imageData, 
                    ImageUri = url, 
                }; 
            } 
        } 

Using ContentResolver (used to access the content model), we resolve URIs to specific content providers. A content provider provides queries to content, in our case image files. We simply create an access query off the main context's ContentResolver instance, and we provide an array of columns for the query to retrieve (for example, file titles, file data, file size, and so on). The first parameter is as follows:

"MediaStore.Images.Media.ExternalContentUri" 

This is used for retrieving the URI to each piece of content returned from the query. Finally, we now have a cursor to iterate through, exactly like an Enumerable, which will loop to the end until there are no more items, and for each iteration we pull the data and URI columns and create a new GalleryItem. You will notice a little trick here with the yield keyword: if we call this function, it will actually return the entire Enumerable from start to finish. Calling the function starts for each-ing over the object; the function is called again until it yields. In the return from calling this function, we get an Enumerable of all the items retrieved from the query as gallery items with image information and local URI.

主站蜘蛛池模板: 蒙城县| 襄樊市| 开阳县| 镇原县| 当雄县| 平昌县| 翁源县| 宾阳县| 广丰县| 射阳县| 寻甸| 乌苏市| 城口县| 博兴县| 双鸭山市| 交口县| 城步| 烟台市| 长丰县| 手机| 林芝县| 山东省| 星座| 鹤庆县| 修水县| 胶州市| 古浪县| 十堰市| 白水县| 灌云县| 尼木县| 开鲁县| 云龙县| 淄博市| 湘乡市| 壶关县| 乌拉特中旗| 温泉县| 瓦房店市| 嘉义市| 临漳县|