├── .gitignore ├── AndroidManifest.xml ├── README ├── build.xml ├── local.properties ├── proguard-project.txt ├── project.properties ├── res ├── drawable-mdpi │ ├── message_read_status_read.png │ └── message_read_status_unread.png ├── drawable │ ├── message_list_item_background.xml │ └── message_read_status.xml ├── layout │ └── message_list_item.xml └── values │ ├── attrs.xml │ └── colors.xml └── src └── com └── charlesharley └── example └── android └── customdrawablestates ├── HomeActivity.java └── MessageListItemView.java /.gitignore: -------------------------------------------------------------------------------- 1 | # IntelliJ IDEA project files 2 | *.iml 3 | .idea 4 | 5 | # Build directories 6 | gen 7 | out 8 | bin 9 | -------------------------------------------------------------------------------- /AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Example Android application demonstrating how to use custom drawable states to enhance your Android application with minimal effort. 2 | 3 | See this tutorial for further details: 4 | 5 | Icons used are from the FatCow icon set, http://www.fatcow.com/free-icons -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 29 | 30 | 31 | 40 | 41 | 42 | 43 | 47 | 48 | 60 | 61 | 62 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /local.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must *NOT* be checked into Version Control Systems, 5 | # as it contains information specific to your local configuration. 6 | 7 | # location of the SDK. This is only used by Ant 8 | # For customization when using a Version Control System, please read the 9 | # header note. 10 | sdk.dir=/Applications/AndroidSDK 11 | -------------------------------------------------------------------------------- /proguard-project.txt: -------------------------------------------------------------------------------- 1 | # To enable ProGuard in your project, edit project.properties 2 | # to define the proguard.config property as described in that file. 3 | # 4 | # Add project specific ProGuard rules here. 5 | # By default, the flags in this file are appended to flags specified 6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt 7 | # You can edit the include path and order by changing the ProGuard 8 | # include property in project.properties. 9 | # 10 | # For more details, see 11 | # http://developer.android.com/guide/developing/tools/proguard.html 12 | 13 | # Add any project specific keep options here: 14 | 15 | # If your project uses WebView with JS, uncomment the following 16 | # and specify the fully qualified class name to the JavaScript interface 17 | # class: 18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 19 | # public *; 20 | #} 21 | -------------------------------------------------------------------------------- /project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-15 15 | -------------------------------------------------------------------------------- /res/drawable-mdpi/message_read_status_read.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CharlesHarley/Example-Android-CustomDrawableStates/a8b9ed7d1fc2bced6b98e1c399416ce4f56b1243/res/drawable-mdpi/message_read_status_read.png -------------------------------------------------------------------------------- /res/drawable-mdpi/message_read_status_unread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CharlesHarley/Example-Android-CustomDrawableStates/a8b9ed7d1fc2bced6b98e1c399416ce4f56b1243/res/drawable-mdpi/message_read_status_unread.png -------------------------------------------------------------------------------- /res/drawable/message_list_item_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | 13 | 14 | 17 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /res/drawable/message_read_status.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /res/layout/message_list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 16 | 17 | 27 | 28 | -------------------------------------------------------------------------------- /res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #ff274172 5 | 6 | -------------------------------------------------------------------------------- /src/com/charlesharley/example/android/customdrawablestates/HomeActivity.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2012 Charles Harley 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package com.charlesharley.example.android.customdrawablestates; 16 | 17 | import android.app.ListActivity; 18 | import android.os.Bundle; 19 | import android.view.View; 20 | import android.view.ViewGroup; 21 | import android.widget.BaseAdapter; 22 | 23 | public class HomeActivity extends ListActivity { 24 | 25 | @Override 26 | public void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | 29 | setListAdapter(new ExampleListAdapter()); 30 | } 31 | 32 | private static class ExampleListAdapter extends BaseAdapter { 33 | 34 | private Message[] messages; 35 | 36 | private ExampleListAdapter() { 37 | messages = new Message[] { 38 | new Message("Gas bill overdue", true), 39 | new Message("Congratulations, you've won!", true), 40 | new Message("I love you!", false), 41 | new Message("Please reply!", false), 42 | new Message("You ignoring me?", false), 43 | new Message("Not heard from you", false), 44 | new Message("Electricity bill", true), 45 | new Message("Gas bill", true), 46 | new Message("Holiday plans", false), 47 | new Message("Marketing stuff", false), 48 | }; 49 | } 50 | 51 | @Override 52 | public int getCount() { 53 | return messages.length; 54 | } 55 | 56 | @Override 57 | public Object getItem(int position) { 58 | return messages[position]; 59 | } 60 | 61 | @Override 62 | public long getItemId(int position) { 63 | return position; 64 | } 65 | 66 | @Override 67 | public View getView(int position, View convertView, ViewGroup viewGroup) { 68 | MessageListItemView messageListItemView = (MessageListItemView) convertView; 69 | 70 | if (messageListItemView == null) { 71 | messageListItemView = new MessageListItemView(viewGroup.getContext()); 72 | } 73 | 74 | Message message = (Message) getItem(position); 75 | messageListItemView.setMessageSubject(message.subject); 76 | messageListItemView.setMessageUnread(message.unread); 77 | 78 | return messageListItemView; 79 | } 80 | 81 | } 82 | 83 | private static class Message { 84 | 85 | private String subject; 86 | private boolean unread; 87 | 88 | private Message(String subject, boolean unread) { 89 | this.subject = subject; 90 | this.unread = unread; 91 | } 92 | 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/com/charlesharley/example/android/customdrawablestates/MessageListItemView.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2012 Charles Harley 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package com.charlesharley.example.android.customdrawablestates; 16 | 17 | import android.content.Context; 18 | import android.util.AttributeSet; 19 | import android.util.DisplayMetrics; 20 | import android.util.TypedValue; 21 | import android.view.LayoutInflater; 22 | import android.view.ViewGroup; 23 | import android.widget.AbsListView; 24 | import android.widget.RelativeLayout; 25 | import android.widget.TextView; 26 | 27 | public class MessageListItemView extends RelativeLayout { 28 | 29 | /** 30 | * Custom message unread state variable for use with a {@link android.graphics.drawable.StateListDrawable}. 31 | */ 32 | private static final int[] STATE_MESSAGE_UNREAD = {R.attr.state_message_unread}; 33 | 34 | private TextView messageSubject; 35 | private boolean messageUnread; 36 | 37 | public MessageListItemView(Context context) { 38 | this(context, null); 39 | } 40 | 41 | public MessageListItemView(Context context, AttributeSet attributeSet) { 42 | super(context, attributeSet); 43 | 44 | loadViews(); 45 | } 46 | 47 | public MessageListItemView(Context context, AttributeSet attributeSet, int defStyle) { 48 | super(context, attributeSet, defStyle); 49 | 50 | loadViews(); 51 | } 52 | 53 | private void loadViews() { 54 | LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 55 | layoutInflater.inflate(R.layout.message_list_item, this, true); 56 | 57 | int fiveDPInPixels = convertDIPToPixels(5); 58 | int fiftyDPInPixels = convertDIPToPixels(50); 59 | 60 | setPadding(fiveDPInPixels, fiveDPInPixels, fiveDPInPixels, fiveDPInPixels); 61 | setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, fiftyDPInPixels)); 62 | setBackgroundResource(R.drawable.message_list_item_background); 63 | 64 | messageSubject = (TextView) findViewById(R.id.message_subject); 65 | } 66 | 67 | public int convertDIPToPixels(int dip) { 68 | // In production code this method would exist in a utility library. 69 | // e.g see my ScreenUtils class: https://gist.github.com/2504204 70 | DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics(); 71 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics); 72 | } 73 | 74 | @Override 75 | protected int[] onCreateDrawableState(int extraSpace) { 76 | // If the message is unread then we merge our custom message unread state into 77 | // the existing drawable state before returning it. 78 | if (messageUnread) { 79 | // We are going to add 1 extra state. 80 | final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); 81 | 82 | mergeDrawableStates(drawableState, STATE_MESSAGE_UNREAD); 83 | 84 | return drawableState; 85 | } else { 86 | return super.onCreateDrawableState(extraSpace); 87 | } 88 | } 89 | 90 | public void setMessageSubject(String subject) { 91 | messageSubject.setText(subject); 92 | } 93 | 94 | public void setMessageUnread(boolean messageUnread) { 95 | // Performance optimisation: only update the state if it has changed. 96 | if (this.messageUnread != messageUnread) { 97 | this.messageUnread = messageUnread; 98 | 99 | // Refresh the drawable state so that it includes the message unread state if required. 100 | refreshDrawableState(); 101 | } 102 | } 103 | 104 | } 105 | --------------------------------------------------------------------------------