Showing posts with label adapters. Show all posts
Showing posts with label adapters. Show all posts

Monday, September 21, 2015

Android Code Review- BaseAdapters are your Friend

Well, I am finally forcing myself to make the jump to iOS, but that is taking more time than I anticipated. More on that in the first iOS blog post. I did spend a little time doing some code review, bug fixing and refactoring. In that process I had a great conversation with one of the other Success Engineers at Xamarin, Colby Williams, who gave me some good feedback and reviewed some of my code. I learned one really great nuget that is worth sharing as it is not immediately apparent when getting started with Xamarin from Android.

When using collection views, it is almost always best to subclass BaseAdapter rather than use any of the out of the box adapters provided by Android (ArrayAdapter comes to mind). I was using the default ArrayAdapter for all of my drop down spinner controls and what I learned is most of the default Adapters provided by Android will give a potential performance hit because for each object in the list, an equal java object has to be created and maintained. This is due to how Xamarin and Android communicate with each other. For an in depth review, this document covers it well:

http://developer.xamarin.com/guides/android/under_the_hood/architecture

So what was happening was for every spinner I created using an Array Adapter, a general C# and Java object were being created and maintained, creating overhead. Instead, creating a BaseAdapter, the under the hood creates a single Java object that has a C# implementation within. Much better performance wise, fewer objects to maintain in memory.

My final code for the adapter is actually pretty simple:

public class SpinnerAdapter : BaseAdapter<string>
    {
        List<string> _items;
        Activity _context;
        public SpinnerAdapter (Activity context, List<string> items)
        {
            _items = items;
            _context = context;
        }

        public override long GetItemId (int position)
        {
            return position;
        }

        public int GetPosition(string selected)
        {
            return _items.IndexOf (selected);
        }

        public override string this[int index] {  
            get { return _items[index]; }
        }

        public override Android.Views.View GetView (int position, Android.Views.View convertView, Android.Views.ViewGroup parent)
        {
            var view = convertView;
            if(view == null)
            {
                view = _context.LayoutInflater.Inflate (Resource.Layout.spinner_item, null);
            }

            view.FindViewById<TextView> (Resource.Id.text).Text = _items[position];

            return view;
        }

        public override int Count {
            get {
                return _items.Count;
            }
        }
    }


Once I made that update, I noticed my views loaded much quicker.

Next time I am going to dig into iOS development, and why it can be a challenge. Given that iOS 9 just came out this past week, I am also using this as an "excuse" to learn iOS 9.

Monday, August 31, 2015

Android ListViews and Updating the Backend Data

I wanted to spend at least one more post to try to put my app in a finished state so there was a lot of time spent on both backend code and getting my skills list to display properly.

My first thought was to use a ListView to display all of my skills, it seemed logical. It was a of items, numbers and a checkbox. Time to brush off what I learned from custom spinner items. Creating adapters for your ListView is actually pretty standard as a lot of the time the default elements are going to not provide what you need.

The first step was to at least get my ListView organized and all my skills displayed. I already had all the hookups in place thanks to my work last week with implementing ViewPager. The first step was to create a new LayoutView, which housed my ListView. I then used a default ArrayAdapter to just grab a placeholder list of strings.


        public override View OnCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
            var root = inflater.Inflate (Resource.Layout.fragment_charsheet_skills, container, false);
            ListView view = root.FindViewById<ListView> (Resource.Id.skillsList);
            var skills = ((CharacterSheetActivity)this.Activity).CharacterInformation.Skills;
           view.Adapter = new ArrayAdapter<string> (this.Activity,Android.Resource.Layout.SimpleListItem1, skills);

            return root;

        }


 Pretty straight forward, and shows at least a starting point:
 

We have a successful ListView! Now to add the Custom Adapter. In this case, I want to display not only the name, but the ability mod score, whether or not the character is trained, and then the final value. (Mod + Proficiency)

The adapter is set up and in order. The first challenge I ran into was getting my items to not clump together. RelativeLayout was not really an option here, so a little searching I discovered one effective option was to use the "Weight" tag to give each item equal weight within the control. 

 





<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="5dp">
    <TextView
        android:id="@+id/skillName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Placeholder"
        android:layout_weight="1" />
    <TextView
        android:id="@+id/modValue"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="+3" />
    <CheckBox
        android:id="@+id/isTrained"
        android:layout_width="32.0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1" />
    <TextView
        android:id="@+id/totalValue"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="+5" />
</LinearLayout>


In theory that worked well, but then I realized I had a problem once I got the final result running.


Looks Perfect
Maybe I need to think this through...
As the weights are row specific, it does not help align each column. Once again, for implementation and times sake I decided to call that "good enough" for now. 

The next step was to work on my back end code. As can be seen in the above, I am hard coding all of my values, and no hookup event for the proficiency checkbox. This actually required a lot of back end work, which was good to revisit and clean up the data.

 Now, I will preface by saying I do not admit this code will be the best. I'm playing more of the functional over proper card here. As a side project it will regularly be a work in progress. 

I created both a Skill and SkillsList class on my back end. The reason for the latter is it gave me a collection that I could house some of the more static concepts like what skills have what ability mods, and a fully populated list of all possible names. Again, not proud of it, but its functional. Then, at launch all possible skills are preloaded based on starting stats. This gives me all my initial conditions that I access as accessor methods. (Loading data I will revisit at a later point).

From an input standpoint the two big changes I needed to make sure happened were 

a) update final values as proficiency was enabled
b) update mods when ability scores are changed.

For the first, I did an inline set of code, pretty straight forward.


proficient.CheckedChange += (object sender, CompoundButton.CheckedChangeEventArgs e) =>  
{ 
    CharacterSheet sheet = ((CharacterSheetActivity)_context).CharacterInformation; 
    
sheet.Skills[position].IsProficient = e.IsChecked;
    skillMod.Text = sheet.Skills [position].TotalValue + ""; 
}


On the flip side, with all of my Attribute code, I added this line:

character.UpdateSkills (Attributes.AttributeName.STR);

Which is essentially calling an update method within my SkillsList:

public void UpdateSkillMod (Attributes.AttributeName type, int mod)
{
    foreach(Skill skill in _skills)
    {                
        if(skill.ModType == type)
        {
            skill.ModValue = mod;
        }
    }
}


This is simply an enumeration through all skills that updates the ModValue of the associated type. That gets us to our final version, which we will hold with for now.

On the back end I also did a lot of cleanup for better preparation for when I need to load character data. At this point I am going to call the Android version of the app in a good spot and now move to get an iOS skin running with this.