Building Adapters
Okay, now that you've dabbled in theory, you can get on with building your very own adapter.
Create a new class by Right-clicking on java/com.raywenderlich.alltherecipes and selecting New > Java Class. Name it RecipeAdapter and define it with the following:
public class RecipeAdapter extends BaseAdapter {
}
You've made the skeleton of the adapter. It extends the
BaseAdapter
class, which requires several inherited methods you'll implement after taking care of one more detail.
Add the following snippet inside the
RecipeAdapter
class:private Context mContext;
private LayoutInflater mInflater;
private ArrayList<Recipe> mDataSource;
public RecipeAdapter(Context context, ArrayList<Recipe> items) {
mContext = context;
mDataSource = items;
mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
In here, you've added the instance variables that will be associated with the adapter and defined a constructor for
RecipeAdapter
.
Your next step is to implement the adapter methods. Kick it off by placing the following code at the bottom of
RecipeAdapter
.//1
@Override
public int getCount() {
return mDataSource.size();
}
//2
@Override
public Object getItem(int position) {
return mDataSource.get(position);
}
//3
@Override
public long getItemId(int position) {
return position;
}
//4
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get view for row item
View rowView = mInflater.inflate(R.layout.list_item_recipe, parent, false);
return rowView;
}
Here's a step-by-step breakdown:
getCount()
lets ListView know how many items to display, or in other words, it returns the size of your data source.getItem()
returns an item to be placed in a given position from the data source, specifically,Recipe
objects obtained frommDataSource
.- This implements the
getItemId()
method that defines a unique ID for each row in the list. For simplicity, you just use the position of the item as its ID. - Finally,
getView()
creates a view to be used as a row in the list. Here you define what information shows and where it sits within the ListView. You also inflate a custom view from the XML layout defined inres/layout/list_item_recipe.xml
-- more on this in the next section.
Defining the Layout of the ListView's Rows
You probably noticed that the starter project comes with the file
res/layout/list_item_recipe.xml
that describes how each row in the ListView should look and be laid out.
Below is an image that shows the layout of the row view and its elements:
Your task is to populate each element of the row view with the relevant recipe data, hence, you’ll define what text goes in the "title" element, the "subtitle" element and so on.
In the
getView()
method, add the following code snippet just before the return statement:// Get title element
TextView titleTextView =
(TextView) rowView.findViewById(com.raywenderlich.alltherecipes.R.id.recipe_list_title);
// Get subtitle element
TextView subtitleTextView =
(TextView) rowView.findViewById(com.raywenderlich.alltherecipes.R.id.recipe_list_subtitle);
// Get detail element
TextView detailTextView =
(TextView) rowView.findViewById(com.raywenderlich.alltherecipes.R.id.recipe_list_detail);
// Get thumbnail element
ImageView thumbnailImageView =
(ImageView) rowView.findViewById(com.raywenderlich.alltherecipes.R.id.recipe_list_thumbnail);
This obtains references to each of the elements (or subviews) of the row view, specifically the title, subtitle, detail and thumbnail.
Now that you've got the references sorted out, you need to populate each element with relevant data. To do this, add the following code snippet under the previous one but before the return statement:
// 1
Recipe recipe = (Recipe) getItem(position);
// 2
titleTextView.setText(recipe.title);
subtitleTextView.setText(recipe.description);
detailTextView.setText(recipe.label);
// 3
Picasso.with(mContext).load(recipe.imageUrl).placeholder(R.mipmap.ic_launcher).into(thumbnailImageView);
Here's what you're doing in the above snippet:
- Getting the corresponding recipe for the current row.
- Updating the row view's text views so they are displaying the recipe.
- Making use of the open-source Picasso library for asynchronous image loading -- it helps you download the thumbnail images on a separate thread instead of the main thread. You're also assigning a temporary placeholder for the
ImageView
to handle slow loading of images.
Note: You should never perform long-running tasks on the main thread. When you do, you expose yourself to the risk of blocking the UI, and that would make scrolling your lists a nightmare!
Now open up MainActivity.java so that you can get rid of the old adapter. In
onCreate
, replace everything below (but not including) this line:final ArrayList<Recipe> recipeList = Recipe.getRecipesFromFile("recipes.json", this);
With:
RecipeAdapter adapter = new RecipeAdapter(this, recipeList);
mListView.setAdapter(adapter);
You just replaced the rather simple
ArrayAdapter
with your own RecipeAdapter
to make the list more informative.
Build and run and you should see something like this:
Now you're cooking for real! Look at those recipes -- thumbnails and descriptions sure make a big difference.
Styling
Now that you've got function under wraps, it's time to turn your attention to the finer things in life. In this case, your finer things are elements that make your app more snazzy, such as compelling colors and fancy fonts.
Start with the fonts. Look for some custom fonts under assets/fonts. You’ll find three font files: JosefinSans-Bold.ttf, JosefinSans-SemiBoldItalic.ttf and Quicksand-Bold.otf.
Open RecipeAdapter.java and go to the
getView()
method. Just before the return statement, add the following:Typeface titleTypeFace = Typeface.createFromAsset(mContext.getAssets(), "fonts/JosefinSans-Bold.ttf");
titleTextView.setTypeface(titleTypeFace);
Typeface subtitleTypeFace =
Typeface.createFromAsset(mContext.getAssets(), "fonts/JosefinSans-SemiBoldItalic.ttf");
subtitleTextView.setTypeface(subtitleTypeFace);
Typeface detailTypeFace = Typeface.createFromAsset(mContext.getAssets(), "fonts/Quicksand-Bold.otf");
detailTextView.setTypeface(detailTypeFace);
In here, you're assigning a custom font to each of the text views in your rows' layout. You access the font by creating a
Typeface
, which specifies the intrinsic style and typeface of the font, by using createFromAsset()
. Next you call setTypeface()
for the corresponding TextView
to set the custom font.
Now build and run. Your result should look like this:
On to sprucing up the colors, which are defined in res/values/colors.xml. Open up RecipeAdapter.java and add the following below the instance variable declarations and above the constructor:
private static final HashMap<String, Integer> LABEL_COLORS = new HashMap<String, Integer>() {{
put("Low-Carb", R.color.colorLowCarb);
put("Low-Fat", R.color.colorLowFat);
put("Low-Sodium", R.color.colorLowSodium);
put("Medium-Carb", R.color.colorMediumCarb);
put("Vegetarian", R.color.colorVegetarian);
put("Balanced", R.color.colorBalanced);
}};
You've created a hash map that pairs a recipe detail label with the resource id of a color defined in colors.xml.
Now go to the
getView()
method, and add this line just above the return statement:detailTextView.setTextColor(ContextCompat.getColor(mContext, LABEL_COLORS.get(recipe.label)));
Working from the inside out:
- Here you get the resource id for the color that corresponds to the
recipe.label
from theLABEL_COLORS
hash map. getColor()
is used inside ofContextCompat
to retrieve the hex color associated with that resource id.- Then you set the color property of the
detailTextView
to the hex color.
Build and run. Your app should look like this:
Nenhum comentário:
Postar um comentário