In the previous article we got a simple app working which is a simple RSS viewer showing a list of recent Styling Android posts, and a detail view when you click on them. In this article we’ll look at applying some material design principles to the app and also try and make it backwards compatible using the appcompat library.
The obvious first stop that we need to do is add the new version of the appcompat library to our build.gradle
:
apply plugin: 'com.android.application' android { compileSdkVersion 21 buildToolsVersion "21.0.2" defaultConfig { applicationId "com.stylingandroid.materialrss" minSdkVersion 14 targetSdkVersion 21 versionCode 1 versionName "1.0" } buildTypes { release { runProguard false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile 'com.mcxiaoke.volley:library:1.0.7' compile "com.android.support:appcompat-v7:21.0.0" }
Let’s begin applying some material deign by looking at the colour scheme that we’re going to use. In the series on Ripples we looked at how easy it it is to apply a colour scheme in the Android-L preview, and it’s every bit as easy using appcompat. However we need to use a different theme than inheriting from the Material themes which are only available on Android-L preview / Lollipop. This is also true for devices which are running Lollipop because these also need to use the appcompat themes – under the covers appcompat detects the underlying OS version and uses the most appropriate mechanisms.
So we need to alter our main app theme:
<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="colorPrimary">@color/sa_green</item> <item name="colorPrimaryDark">@color/sa_green_dark</item> <item name="colorAccent">@color/sa_green</item> </style> </resources>
One thing worthy of note is that don’t include the android namespace on either the parent theme or the item names. This is because they are defined within our app’s default namespace because the appcompat library is imported in to our project and so we don’t have to address them as being part of the OS.
We also need to define the colour definitions:
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="text_dark">#333333</color> <color name="text_medium">#555555</color> <color name="text_light">#888888</color> <color name="sa_green">#1E9618</color> <color name="sa_green_dark">#146310</color> </resources>
We also need to delete the v21 styles that we defined previously because, as we mentioned, the appcompat library will manage this for us and we don’t need separate styles.
If we run this on a Lollipop device everything looks great – we have our colour themes in place:
However there are a couple of problems if we run it on KitKat:
Firstly, the status bar is still showing black. There’s nothing that we can do about this because extending our colour scheme outside of our app and on to system controls is simply not supported pre-Lollipop, so we’ll just have to live with it.
The second problem is that the ActionBar has disappeared! It was certainly there before we applied the appcompat theme. The reason for this is that to apply the theme and support the ActionBar our Activity must extend ActionBarActivity. This is pretty simple for FeedDetailView which currently extends Activity but requires a little more work on FeedListActivity which currently extends ListActivity:
public class FeedListActivity extends ActionBarActivity implements FeedConsumer { private static final String DATA_FRAGMENT_TAG = DataFragment.class.getCanonicalName(); private ListView listView; private FeedAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(android.R.layout.list_content); listView = (ListView) findViewById(android.R.id.list); listView.setOnItemClickListener(new ItemClickListener()); DataFragment dataFragment = (DataFragment) getFragmentManager().findFragmentByTag(DATA_FRAGMENT_TAG); if (dataFragment == null) { dataFragment = (DataFragment) Fragment.instantiate(this, DataFragment.class.getName()); dataFragment.setRetainInstance(true); FragmentTransaction transaction = getFragmentManager().beginTransaction(); transaction.add(dataFragment, DATA_FRAGMENT_TAG); transaction.commit(); } } public void setFeed(Feed feed) { adapter = new FeedAdapter(this, feed.getItems()); listView.setAdapter(adapter); } @Override public void handleError(String message) { Toast.makeText(this, message, Toast.LENGTH_LONG).show(); } private class ItemClickListener implements AdapterView.OnItemClickListener { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Intent detailIntent = new Intent(FeedListActivity.this, FeedDetailActivity.class); Item item = adapter.getItem(position); detailIntent.putExtra(FeedDetailActivity.ARG_ITEM, item); startActivity(detailIntent); } } }
Essentially we have to manually implement some of the functionality that we got from ListActivity, but there really isn’t that much required.
There’s one last thing worth mentioning: We get different item selection visual feedback on Lollipop compared to older versions of Android even when using the appcompat library. On Lollipop we get a nice ripple effect (as was discussed in the series on Ripples):
Whereas on KitKat we get a standard ListView click animation:
The reason for this is purely that Ripples are not included in the appcompat library, and utilise the render thread that has been introduced with Lollipop. It wouldn’t play well with other animations without the render thread, so has not been included.
In the next article we’ll take a further look in to some of the backwardly compatible material design stuff that we can do with the appcompat library.
The source code for this article is available here.
© 2014, Mark Allison. All rights reserved.
Copyright © 2014 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.
Hey, nice article.
I see that the “compatibility burden” will never go away heh. Now we need to rewrite stuff to have lollipop features. At least we don’t have to support API 9 anymore.
Hi, Nice one,
I am implementing Meterial using eclipse+ADT with your articles and able run the Part 1.And while changing the part 2 as Application Theme in styles like below
Eclipse unable to build the application instead showing
error: Error retrieving parent for item: No resource found that matches the given name ‘Theme.AppCompat.Light.DarkActionBar’.
Can you please let me know how to solve the above error.
Thanks,
It sounds like you haven’t added the necessary support library to your project.