20 November 2014

Android Studio Keyboard shortcuts


I've previously blogged about my frustrations moving from Eclipse to Android Studio. Largely this extends from a re-work of the keyboard mappings. I've used Eclipse for many years and have become very reliant on my keyboard short-cuts to speed up my work.

Recently I discovered the ability to make Android Studio run using Eclipse shortcuts. This literally changed my whole working day and has made me so happy!

Simply click File -> Settings then either scroll down to keymap or search for keymap.

In the dropdown just change the keyboard shortcuts to Eclipse:


Brilliant :D

Lollipop with Nexus 7 (2012)

Just a quick one to anyone who owns the 2012 version of the Nexus 7. DON'T upgrade to Lollipop.
I got the Over the Air (OTA) update last week and put it off for a few days as I don't like to rush. Eventually I got fed up with the reminders and went ahead.

Well what a disaster, its made my Nexus 7 almost unusable. The Keyboard doesn't open for sometimes up to a minute, very few of the apps run at all and its generally slow and un-responsive. 

This is a crying shame as I think Lollipop is a great release, I like the direction Android has taken. Its just a real shame Google didn't test this before pushing it out to everyone.

05 November 2014

Android ripple effect in Lollipop

Just a quick how-to today.

So Google have made a big thing of the new ripple effect in Android Lollipop.
http://android-developers.blogspot.co.uk/2014/10/implementing-material-design-in-your.html

 To my eyes however it wasn't immediately clear how to implement it, particularly if you wanted your app to work in older versions of Android, which almost everyone will.

So the first point to note is android buttons automatically implement the ripple effect if you've got the material theme running. However I mostly used TextViews as buttons with custom drawables for their background and onClick state.

Second I'm not talking about getting ripple working in old versions of Android, just a graceful rollback to a pressed state.

So first create a drawable and a drawable-v21 folder under res, in each add a button_selector.xml

res/drawable/button_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Pressed -->
    <item android:state_pressed="true" android:drawable="@color/colorPrimaryDark" />
    <!-- Selected -->
    <item android:state_selected="true" android:drawable="@color/colorPrimaryDark" />
    <!-- Focus -->
    <item android:state_focused="true" android:drawable="@color/colorPrimaryDark" />
    <!-- Default -->
    <item android:drawable="@color/colorPrimary"/>
</selector>

res/drawable-v21/button_selector.xml
<ripple
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="@color/accent">
    <!-- Pressed -->
    <item android:state_pressed="true" android:drawable="@color/colorPrimaryDark" />
    <!-- Selected -->
    <item android:state_selected="true" android:drawable="@color/colorPrimaryDark" />
    <!-- Focus -->
    <item android:state_focused="true" android:drawable="@color/colorPrimaryDark" />
    <!-- Default -->
    <item android:drawable="@color/colorPrimary"/>
</ripple>

now in your activity_main or wherever you need to add a button with a background

<TextView
    android:id="@+id/activity_main_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:background="@drawable/button_selector"
    android:padding="20dp"
    android:text="Button"/>

You'll notice the ripple xml basically incorporates the same drawables as the selector does, adding on the ripple effect.

04 November 2014

Android CursorAdapter


So the other day some stack overflow numpty yelled at me that I should use a cursor adapter instead of an array adapter. Well, just a point of habbit I thought. However I'd not extensively used a cursor adapter before, so I thought I'd give it a shot before I dismissed his suggestion.

Anyway I decided to come up with a simple example as there aren't that many examples of straight cursorAdapters out there and simpleCursorAdapters just don't cut it. The basic idea of a cursor adapter is to directly link to database adapter, rather than say an array adapter, which displays an array of any type of data.

So first grab you data, obviously this can be whatever you want. I copied a Google contacts example and simplified it.

This is inside a Fragment:

    @SuppressLint("InlinedApi") 
    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        
        @SuppressLint("InlinedApi")
        final String SORT_ORDER = Utils.hasHoneycomb() ? Contacts.SORT_KEY_PRIMARY : Contacts.DISPLAY_NAME;
        
        @SuppressLint("InlinedApi")
        final String[] PROJECTION = {
                Contacts._ID,
                Contacts.LOOKUP_KEY,
                Utils.hasHoneycomb() ? Contacts.DISPLAY_NAME_PRIMARY : Contacts.DISPLAY_NAME,
                Utils.hasHoneycomb() ? Contacts.PHOTO_THUMBNAIL_URI : Contacts._ID
        };
        
        final String SELECTION =
                (Utils.hasHoneycomb() ? Contacts.DISPLAY_NAME_PRIMARY : Contacts.DISPLAY_NAME) +
                "<>''" + " AND " + Contacts.IN_VISIBLE_GROUP + "=1";
        
        if(id == QUERY_ID){
            return new CursorLoader(getActivity(),
                    Contacts.CONTENT_URI,
                    PROJECTION,
                    SELECTION,
                    null,
                    SORT_ORDER);
        }
        
        return null;
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        if (loader.getId() == QUERY_ID) {
            mAdapter.swapCursor(data);
            mAdapter.notifyDataSetChanged();
        }
    }


Then we need our adapter:

public class AdapterNewContacts extends CursorAdapter{
    
    private LayoutInflater mInflater;
    
    public AdapterNewContacts(Context context) {
        super(context, null, 0);

        mInflater = LayoutInflater.from(context);
    }
    
    @SuppressLint("InlinedApi") 
    public void bindView(View view, Context context, Cursor cursor) {
        
        final ViewHolder holder = (ViewHolder) view.getTag();
        
        holder.textName.setText(
            cursor.getString(cursor.getColumnIndex((Utils.hasHoneycomb() ? Contacts.DISPLAY_NAME_PRIMARY : Contacts.DISPLAY_NAME)))
        );
    }

    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        View convertView = mInflater.inflate(R.layout.contacts_list_item, parent, false);
        
        final ViewHolder holder = new ViewHolder();
        holder.textName = (TextView) convertView.findViewById(R.id.contacts_list_item_name);
        
        convertView.setTag(holder);
        return convertView;
    }
    
    private class ViewHolder {
        TextView textName;
    }
}



That's it! Notice the difference between this and our normal ArrayAdapter, there's no getView() and we actually have one method for creating the view and one for re-aquiring the view. This is different from a getView where we have to create and get views in one method.

So pretty simple. I still don't agree with whoever suggested this though, it's too restrictive. In an ArrayAdapter I can create my own dataset and update it however I want. With this example I'm limited to only the one cursor.

Oh well, it was an interesting experiment.