Click here to Skip to main content
13,589,916 members
Click here to Skip to main content
Add your own
alternative version


30 bookmarked
Posted 29 Apr 2014
Licenced CPOL

A Custom Grid-List Adapter

, 29 Apr 2014
Rate this:
Please Sign up or sign in to vote.
Making a custom ListAdapter


In this article, I will explain how I made my custom list-adapter for a list activity, to take benefits if you are interested in.

I needed a grid that looks different from all classic ones, I wanted two columns to load my Card Items but with no row alignment:


We will use the BaseAdapter and ListView, to make the hybrid look of the Cards gridView.

Using the Code

I. CardItem

First of all, let's define the Card Item sample.
It is composed of:

  • Title
  • Subtitle
  • Logo
internal class CardItem
     public string Title { get; set; }
     public string SubTitle { get; set; }
     public int ResId { get; set; }

Its corresponding layout is as follows:

<LinearLayout xmlns:android=""
        android:id="@+id/card_title" />
        android:textColor="#6D7B8D" />
        android:padding="5dp" />

II. Card Item BaseAdapter

The next step is to implement our BaseAdapter<IList<CardItem>> in our custom usage.

The custom view is similar to a gridView layout.

I created a RowView layout containing 4 cards layout items, and each card of them will have its random defined height. this screen shot will explain:

Cards Layouts will have a single random width/height at creation, and will be stored, to get them back on scroll events.

This row layout is loaded dynamically in the CardsViewMaker.cs class, GenerateView(int position) will generate the cardItemsArray[position] layout.

// Generate empty raw layout with no sizing and data..
public View GenerateView(int position)
            //Call the inflater to inflate CardItem.axml Layout later and get their View..
            var inflater = _context.LayoutInflater;

            /* Our Row Layout: Horizontal Layout containing 2 Vertical Layout
               [   E1   ][   E2  ]
               [   E3   ][   E4  ]
            // It will be the root view element, Linear Horizontal Layout

            var rootView = new LinearLayout(_context)
                Orientation = Orientation.Horizontal
            var rootParams = new AbsListView.LayoutParams
            (ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.WrapContent);
            rootView.LayoutParameters = rootParams;
            var card1 = (LinearLayout)inflater.Inflate(Resource.Layout.CardItem, null);
            var card2 = (LinearLayout)inflater.Inflate(Resource.Layout.CardItem, null);
            var card3 = (LinearLayout)inflater.Inflate(Resource.Layout.CardItem, null);
            var card4 = (LinearLayout)inflater.Inflate(Resource.Layout.CardItem, null);
            card1.Id = 1;
            card2.Id = 2;
            card3.Id = 3;
            card4.Id = 4;
            var leftView = new LinearLayout(_context)
                Orientation = Orientation.Vertical
            var leftParams = new AbsListView.LayoutParams
            (ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
            leftView.LayoutParameters = leftParams;
            var rightView = new LinearLayout(_context)
                Orientation = Orientation.Vertical
            var rightParams = new AbsListView.LayoutParams
            (ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
            rightView.LayoutParameters = rightParams;
            return rootView;

Now once the row (view) is generated (or recycled), it's ready to give it its new random size if it's newly created, or give it back its original size on generation, it's in the ReSize(View view, int position) and SetViewInfo(View view, int id, int position) functions:

//used to store the generated sizes for each card item 
private readonly Dictionary<int, string> _sizes = new Dictionary<int, string>();  
//check if the card is alread fetched before, to give it back original generated size
public void ReSize(View view, int position)
            if (_sizes.ContainsKey(position))
                var gens = _sizes[position].Split(' ').Select(int.Parse);
                var enumerable = gens as int[] ?? gens.ToArray();
                SetSize(view, enumerable.ElementAt(0), enumerable.ElementAt(1));
            {//random values use to generate the CardItemLayout random size...
                var leftrnd = _gen.Next(30, 70);
                var rightrnd = _gen.Next(30, 70);
                SetSize(view, leftrnd, rightrnd);//Sizing function
                _sizes.Add(position, string.Format("{0} {1}", leftrnd, rightrnd));

            var id = 4*position;

            SetViewInfo(view, 1, id);
            SetViewInfo(view, 3, id+1);
            SetViewInfo(view, 2, id+2);
            SetViewInfo(view, 4, id+3);

//leftRand and rightRand are random numbers used to generate cards height
public void SetSize(View view, int leftRand, int rightRand)
            //get the screen width to divide half half cards with minus padding...
            var width = _context.WindowManager.DefaultDisplay.Width; 
            var card1 = view.FindViewById<LinearLayout>(1);
            var card2 = view.FindViewById<LinearLayout>(2);
            var card3 = view.FindViewById<LinearLayout>(3);
            var card4 = view.FindViewById<LinearLayout>(4);
            var bh = DefaultHeight + Pad;
            var bh1 = (int)(bh * leftRand / 100.0f - Pad / 2.0f);
            var bh2 = (int)(bh * (1 - leftRand / 100.0f) - Pad / 2.0f);
            //defining layout parameters for our cards
            var params1 = new LinearLayout.LayoutParams(width / 2 - 3 * Pad / 4, bh1);
            var params2 = new LinearLayout.LayoutParams(width / 2 - 3 * Pad / 4, bh2);
            bh1 = (int)(bh * rightRand / 100.0f - Pad / 2.0f);
            bh2 = (int)(bh * (1 - rightRand / 100.0f) - Pad / 2.0f);
            var params3 = new LinearLayout.LayoutParams(width / 2 - 3 * Pad / 4, bh1);
            var params4 = new LinearLayout.LayoutParams(width / 2 - 3 * Pad / 4, bh2);
            //Set the right geometric margin dimensions...
            params1.SetMargins(Pad / 2, Pad / 4, 0, Pad / 4);
            params2.SetMargins(Pad / 2, Pad / 4, 0, 0);
            params3.SetMargins(Pad / 2, Pad / 2, 0, Pad / 4);
            params4.SetMargins(Pad / 2, Pad / 4, 0, 0);
            //setting our layout parameters
            card1.LayoutParameters = params1;
            card2.LayoutParameters = params2;
            card3.LayoutParameters = params3;
            card4.LayoutParameters = params4;
//used to fill our cards with some data
private void SetViewInfo(View view, int id, int position)
            //if the position of resultant card is higher than the data items count
           // do not fill the card, hide it.
             if (position >= _items.Count)
                view.FindViewById(id).Visibility = ViewStates.Invisible;
            { // setting title, subtitle and logo 
                var card = _items[position];
                (Resource.Id.card_title).Text = card.Title; 
                (Resource.Id.card_subtitle).Text = card.SubTitle; 
                view.FindViewById(id).Visibility = ViewStates.Visible;

Now let's dig into the CardItemsAdapter that implements the BaseAdapter.

It is important to know that override int Count must returns the rows count for the listview.

So if we have "X" card elements, we will have at least X/4 rows if X is a multiple of 4, else we'll have 1 + X/4 rows.

The override override View GetView(int position, View convertView, ViewGroup parent) will return the row view layout to the listview when scrolling or fetching rows data.
We must recycle fetched views, and reuse them for better performance and memory usage.

internal class CardItemsAdapter : BaseAdapter<CardItem>
        private readonly IList<CardItem> _values;
        private readonly CardsViewMaker _gen;
        public CardItemsAdapter(Activity context, IList<CardItem> values)
            _gen = new CardsViewMaker(context,values);
            _values = values;
        public CardItemsAdapter(Activity context,int height, int spacing, IList<CardItem> values)
            _gen = new CardsViewMaker(context,height, spacing, values);
            _values = values;
        public override CardItem this[int position]
            get { return _values[position]; }
        public override long GetItemId(int position)
            return position;
        public override View GetView(int position, View convertView, ViewGroup parent)
       //check if convertView is null => Generate a new View
       // if it's not null, recycle, reuse it..
           var view = convertView ?? _gen.GenerateView(position);
          // tell the CardViewMaker _gen to Resize and fill the card data
          _gen.ReSize(view, position); 
            return view;
        public override int Count
                return _values.Count / 4 + (_values.Count % 4 == 0 ? 0 : 1);

Finally, let's test the Cards adapter in a ListActivity:

var mAdapter = CardItem.GenerateSampleCardItems();//Generate Sample Cards Data
ListAdapter = new CardItemsAdapter(this, mAdapter ); 
ListView.DividerHeight = 0; // Hide the divider between the rows       

// it's the manifest, compile with ics,15 minimum sdk, with light theme.
// remove android:theme="@android:style/Theme.DeviceDefault.Light" for a lower version.. 
<manifest xmlns:android="" 
android:versionCode="1" android:versionName="1.0" package="CustomAdapter.CustomAdapter">
    <uses-sdk android:targetSdkVersion="15" android:minSdkVersion="15" />
    <application android:theme="@android:style/Theme.DeviceDefault.Light" 
android:label="CustomAdapter" android:icon="@drawable/icon"></application>

Points of Interest

Hope it's a handy work that you will use. Thanks.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Fady Aro
Lebanon Lebanon
Electronics, Telecommunications, Development, Music!

You may also be interested in...


Comments and Discussions

QuestionQuestion Pin
wipar27-Jun-14 12:42
memberwipar27-Jun-14 12:42 
AnswerRe: Question Pin
F. ARO29-Jun-14 4:49
memberF. ARO29-Jun-14 4:49 
GeneralRe: Question Pin
wipar14-Jul-14 14:50
memberwipar14-Jul-14 14:50 
GeneralRe: Question Pin
F. ARO15-Jul-14 12:36
memberF. ARO15-Jul-14 12:36 
QuestionThanks - Great Example Pin
WalterHPalladino11-Jun-14 2:10
memberWalterHPalladino11-Jun-14 2:10 
AnswerRe: Thanks - Great Example Pin
F. ARO13-Jun-14 22:03
memberF. ARO13-Jun-14 22:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web02-2016 | 2.8.180618.1 | Last Updated 29 Apr 2014
Article Copyright 2014 by Fady Aro
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid