65.9K
CodeProject 正在变化。 阅读更多。
Home

自定义 Grid-List 适配器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.66/5 (20投票s)

2014年4月29日

CPOL

2分钟阅读

viewsIcon

30104

downloadIcon

1042

制作自定义 ListAdapter。

引言

在本文中,我将解释我是如何为列表活动制作自定义列表适配器的,如果您感兴趣,可以从中受益。

我需要一个与所有经典网格不同的网格,我想要两列来加载我的卡片项目,但不需要行对齐

背景

我们将使用 BaseAdapter ListView,来制作卡片 gridView 的混合外观。

Using the Code

一、卡片项目

首先,让我们定义卡片项目示例。
它由以下部分组成:

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

其对应的布局如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_margin="16dp"
    android:layout_height="wrap_content"
    android:background="@drawable/card_background">
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingTop="4dp"
        android:paddingRight="4dp"
        android:paddingLeft="4dp"
        android:text="Title"
        android:textSize="18dp"
        android:textColor="@android:color/black"
        android:id="@+id/card_title" />
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingRight="6dp"
        android:paddingLeft="6dp"
        android:text="Subtitle"
        android:textSize="14dp"
        android:id="@+id/card_subtitle"
        android:textColor="#6D7B8D" />
    <ImageView
        android:id="@+id/card_logo"
        android:layout_marginTop="8dp"
        android:layout_marginBottom="4dp"
        android:layout_width="fill_parent"
        android:layout_height="match_parent"
        android:background="@drawable/custom_background"
        android:layout_gravity="center"
        android:src="@drawable/android"
        android:scaleType="centerInside"
        android:padding="5dp" />
</LinearLayout>

二、卡片项目 BaseAdapter

下一步是在我们的自定义用法中实现我们的 BaseAdapter<IList<CardItem>>

自定义视图类似于 gridView 布局。

我创建了一个 RowView 布局,其中包含 4 个卡片布局项,并且每个卡片都有其随机定义的高度。这张截图会说明:

卡片布局将在创建时具有单个随机宽度/高度,并将被存储,以便在滚动事件中获取它们。

此行布局在 CardsViewMaker.cs 类中动态加载,GenerateView(int position) 将生成 cardItemsArray[position] 布局。

// 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;
            leftView.AddView(card1);
            leftView.AddView(card2);
            var rightView = new LinearLayout(_context)
            {
                Orientation = Orientation.Vertical
            };
            var rightParams = new AbsListView.LayoutParams
            (ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
            rightView.LayoutParameters = rightParams;
            rightView.AddView(card3);
            rightView.AddView(card4);
            rootView.AddView(leftView);
            rootView.AddView(rightView);
            rootView.SetBackgroundColor(Android.Graphics.Color.ParseColor("#FFE5E5E5"));
            return rootView;
        }   

现在,一旦行(视图)生成(或回收),它就可以为其赋予新的随机大小(如果它是新创建的),或者在生成时将其恢复到原始大小。这在 ReSize(View view, int position)SetViewInfo(View view, int id, int position) 函数中。

//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));
            }
            else
            {//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;
            else 
            { // setting title, subtitle and logo 
                var card = _items[position];
                view.FindViewById(id).FindViewById<TextView>
                (Resource.Id.card_title).Text = card.Title; 
                view.FindViewById(id).FindViewById<TextView>
                (Resource.Id.card_subtitle).Text = card.SubTitle; 
                view.FindViewById(id).FindViewById<ImageView>
                (Resource.Id.card_logo).SetImageResource(card.ResId);
                view.FindViewById(id).Visibility = ViewStates.Visible;
            } 
        }       

现在让我们深入研究 CardItemsAdapter ,它实现了 BaseAdapter

重要的是要知道 override int Count 必须返回 listview 的行数。

所以,如果我们有“X”个卡片元素,如果 X 是 4 的倍数,我们将至少有 X/4 行,否则我们将有 1 + X/4 行。

覆盖 override View GetView(int position, View convertView, ViewGroup parent) 将在滚动或获取行数据时将行视图布局返回给 listview
我们必须回收获取的视图,并重用它们以获得更好的性能和内存使用率。

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
        {
            get
            {
                return _values.Count / 4 + (_values.Count % 4 == 0 ? 0 : 1);
            }
        }
    }    

最后,让我们在 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="http://schemas.android.com/apk/res/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>
</manifest>  

关注点

希望这能成为您有用的工作。谢谢。

© . All rights reserved.