Material Design ListView Header With Xamarin.Android




As we know android always improves his weakness in his design but since Android 5 was introduced the Material design was created to fix that and now no only we can create useful Android apps now we are able to create beautiful apps too, in this tutorial we will create a simple listview with an awesome Material Design Header using backward libraries to support old android versions.

So lets start.

First download these two libs from nuget

Floating Action Button for Xamarin.Android

Xamarin Support Library v7 AppCompat

After add these libraries to the project we need to add these 3 files to our Layout folder

“FakeHeader.axml” Contains the fake detail header you can customize it with the elements that you want


<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <RelativeLayout
        android:id="@+id/header_container"
        android:layout_width="match_parent"
        android:layout_height="@dimen/header_height"
        android:layout_marginBottom="30dp"
        android:background="@drawable/background">
        <RelativeLayout
            android:id="@+id/header_infos_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:padding="16dp">
            <TextView
                android:id="@+id/header_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_toRightOf="@+id/header_picture"
                android:text="Toolbar Title"
                android:textColor="@android:color/white" />
            <TextView
                android:id="@+id/header_subtitle"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/header_title"
                android:layout_toRightOf="@+id/header_picture"
                android:text="Toolbar Subtitle"
                android:textColor="@android:color/white" />
        </RelativeLayout>
    </RelativeLayout>
    <com.refractored.fab.FloatingActionButton
        android:id="@+id/header_fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|right"
        android:src="@android:drawable/ic_dialog_info"
        android:layout_marginLeft="10dp"
        android:layout_marginBottom="5dp"
        android:layout_marginRight="10dp"
        android:background="@android:color/transparent" />
</FrameLayout>



“toolbar.xaml” Simple support toolbar V7 implementation


<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:local="http://schemas.android.com/apk/res-auto"
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:minHeight="?attr/actionBarSize"
    android:background="?attr/colorPrimary"
    android:layout_height="@dimen/action_bar_height"
    local:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    local:popupTheme="@style/ThemeOverlay.AppCompat.Light" />



“Main.axml” Contains the UI for our main activity


<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <ListView
        android:minWidth="25px"
        android:minHeight="25px"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/listView1"
        android:scrollbars="none" />
    <include
        android:id="@+id/toolbar"
        layout="@layout/toolbar" />
</FrameLayout>

Now we need to set the Colors, Dimensions and Styles Material Design Like.
In our Values folder we need to add this 3 files. 


“Colors.xml” Contains the colors that we use in our designs


<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="primary">#3498DB</color>
    <color name="primary_pressed">#2C3E50</color>
    <color name="ripple">#ff80ab</color>
    <color name="accent">#E64A19</color>
    <color name="accent_pressed">#BF360C</color>
</resources>



“Dimens.xml” Contains the dimensions of our fake header


<?xml version="1.0" encoding="utf-8"?>
<resources>
   <!-- FAKE HEADER HEIGHT -->
   <dimen name="header_height">230dp</dimen>
   <!-- Default Action Bar Height Default Android Value-->
   <dimen name="action_bar_height">56dp</dimen>
   <!-- Default Toolbar Left Margin Default Android Value-->
   <dimen name="toolbar_left_margin">72dp</dimen>
</resources>


“Style.xml” Contain our Material Design Theme


<?xml version="1.0" encoding="utf-8" ?>
<resources>
  <style name="MyTheme" parent="MyTheme.Base">
  </style>
  <!-- Base theme applied no matter what API -->
  <style name="MyTheme.Base" parent="Theme.AppCompat.NoActionBar">
    <item name="android:windowNoTitle">true</item>
    <!--We will be using the toolbar so no need to show ActionBar-->
    <item name="windowActionBar">false</item>
    <!-- Set theme colors from http://www.google.com/design/spec/style/color.html#color-color-palette-->
    <!-- colorPrimary is used for the default action bar background -->
    <item name="colorPrimary">#2196F3</item>
    <!-- colorPrimaryDark is used for the status bar -->
    <item name="colorPrimaryDark">#1976D2</item>
    <!-- colorAccent is used as the default value for colorControlActivated
         which is used to tint widgets -->
    <item name="colorAccent">#FF4081</item>
      <!-- You can also set colorControlNormal, colorControlActivated
         colorControlHighlight and colorSwitchThumbNormal. -->
  </style>
</resources>



Also we can add a menu file into our Menu folder “home.xml”


<?xml version="1.0" encoding="utf-8" ?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" 
      xmlns:local="http://schemas.android.com/apk/res-auto">
 <item
      android:id="@+id/menu_share"
      android:icon="@drawable/ic_action_social_share"
      local:showAsAction="ifRoom"
      android:title="Share" />
 <item
      android:id="@+id/menu_settings"
      local:showAsAction="never"
      android:title="Settings" />
</menu>


Now we continue with the code


First we need to implement “ActionbarActivity” instead “Activity” in our main activity to keep Android 5 features in old versions also we set the theme and IOnScrollListener implementation


[Activity (Label = "ExpandibleHeader", MainLauncher = true, Theme = "@style/MyTheme", Icon = "@drawable/icon")]
    public class MainActivity : ActionBarActivity, AbsListView.IOnScrollListener



Properties/Views that we need to set up our ListView Header:


// The height of your fully expanded header view (same than in the xml layout)
        int HeaderHeight { get; set; }
        int MinHeaderTranslation { get; set; }

        // Header views
        View HeaderView { get; set; }
        TextView HeaderTitle { get; set; }
        TextView HeaderSubtitle { get; set; }
        FloatingActionButton HeaderFab { get; set; }
        Toolbar Toolbar { get; set; }

        //Main Listview
        ListView ListView { get; set; }


The logic for the IOnScrollListener gives us all callbacks when the user scrolls the listview we make all the magic on this part:


#region AbsListView.IOnScrollListener Implementation
        public void OnScroll (AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
        {
            int scrollY = getScrollY(view);

            // This will collapse the header when scrolling, until its height reaches
            // the toolbar height
            HeaderView.TranslationY = Math.Max(0, scrollY + MinHeaderTranslation);

            // Scroll ratio (0 <= ratio <= 1). 
            // The ratio value is 0 when the header is completely expanded, 
            // 1 when it is completely collapsed
            float offset = 1 - Math.Max(
                (float) (-MinHeaderTranslation - scrollY) / -MinHeaderTranslation, 0f);

            // Update items alpha from offset
            UpdateItemsAlpha(offset);
        }

        public void OnScrollStateChanged (AbsListView view, ScrollState scrollState)
        {
            //throw new NotImplementedException ();
        }

        void UpdateItemsAlpha(float offset)
        {
            // Alpha 255=100%, 0=0%
            Toolbar.Background.Alpha = (int)((offset)*255);

            // Alpha 1=100%, 0=0%
            HeaderFab.Alpha = 1 - offset;
            HeaderTitle.Alpha = 1 - offset;
            HeaderSubtitle.Alpha = 1 - offset;
        }

        // Method that allows us to get the scroll Y position of the ListView
        public int getScrollY(AbsListView view)
        {
            View c = view.GetChildAt(0);
            if (c == null)
                return 0;
            int firstVisiblePosition = view.FirstVisiblePosition;
            int top = c.Top;
            int headerHeight = 0;
            if (firstVisiblePosition >= 1)
                headerHeight = this.HeaderHeight;
            return -top + firstVisiblePosition * c.Height + headerHeight;
        }

        #endregion



Activity Menu logic:


/// <Docs>The options menu in which you place your items.</Docs>
        /// <returns>To be added.</returns>
        /// <summary>
        /// This is the menu for the Toolbar/Action Bar to use
        /// </summary>
        /// <param name="menu">Menu.</param>
        public override bool OnCreateOptionsMenu (IMenu menu)
        {
            MenuInflater.Inflate (Resource.Menu.home, menu);
            return base.OnCreateOptionsMenu (menu);
        }

        public override bool OnOptionsItemSelected (IMenuItem item)
        {    
            switch (item.ItemId)
            {
            case Resource.Id.menu_share:
                Intent intent = new Intent(Intent.ActionSend);
                intent.SetType("text/plain");
                intent.PutExtra(Intent.ExtraText, "http://www.alejandroruizvarela.blogspot.com");
                intent.PutExtra(Android.Content.Intent.ExtraSubject, "Awesome Material Design example¡¡¡");
                StartActivity(Intent.CreateChooser(intent, "Share"));
                return true;
            }
            return base.OnOptionsItemSelected (item);
        }




After this now we can set up all our views and properties in the OnCreate Override method:


protected override void OnCreate (Bundle savedInstanceState)
        {
            base.OnCreate (savedInstanceState);

            // Set our view from the "main" layout resource
            SetContentView (Resource.Layout.Main);

            Toolbar = FindViewById<Toolbar>(Resource.Id.toolbar);
            //Toolbar will now take on default actionbar characteristics
            SetSupportActionBar (Toolbar);
            SupportActionBar.Title = "San Juan de los Lagos";

            HeaderHeight = Resources.GetDimensionPixelSize(Resource.Dimension.header_height);

            ListView = FindViewById<ListView> (Resource.Id.listView1);

            //Create a dummy data
            var items = new string[] { "San Juan de los Lagos","Lagos de Moreno","Zapopan", "Guadalajara",
                "Jalostotitlan", "Ajijic", "Cocula","Ciudad Guzmán","La Barca", "Ocotlán", "Puerto Vallarta",
                "Tepatitlán de Morelos"
            };

            //create a new simple adapter for our main listview
            var ListAdapter = new ArrayAdapter<String>(this, Android.Resource.Layout.SimpleListItem1, items);
            ListView.Adapter = ListAdapter;
            ListView.ItemClick += ListView_ItemClick;

            // Init the MinHeaderTranslation values
            MinHeaderTranslation = -HeaderHeight + 
                Resources.GetDimensionPixelOffset(Resource.Dimension.action_bar_height);

            // Inflate your header view
            HeaderView = LayoutInflater.Inflate(Resource.Layout.FakeHeader, ListView, false);

            // Retrieve the header views
            HeaderTitle = (TextView) HeaderView.FindViewById(Resource.Id.header_title);
            HeaderTitle.Text = "Downtown";
            HeaderSubtitle = (TextView) HeaderView.FindViewById(Resource.Id.header_subtitle);
            HeaderSubtitle.Text = "Night Life";
            HeaderFab = (FloatingActionButton) HeaderView.FindViewById(Resource.Id.header_fab);
            HeaderFab.ColorNormal = Resources.GetColor(Resource.Color.primary);
            HeaderFab.ColorPressed = Resources.GetColor(Resource.Color.primary_pressed);
            HeaderFab.Click += HeaderFab_Click;

            // Add the headerView to your listView
            ListView.AddHeaderView(HeaderView, null, false);
            ListView.SetOnScrollListener (this);
        }

        void HeaderFab_Click (object sender, EventArgs e)
        {
            Toast.MakeText (this, "Floating Action Button Clicked", ToastLength.Short).Show();
        }

        void ListView_ItemClick (object sender, AdapterView.ItemClickEventArgs e)
        {
            //Smooth scroll to the top of listview
            ListView.SmoothScrollToPosition (0);
        }




After this now we can see the result a beautiful Material design header with old android versions compatibility.


URL  References:




http://developer.xamarin.com/samples/android/Support%20v7/

You can download a full example code from here:

Comentarios

Publicar un comentario

Entradas populares de este blog

Bluetooth Arduino + Xamarin.Android

Simple ListView with Xamarin.Forms

Xamarin.Forms: Get native image from ImageSource