Dreaming of Google I/O: ADT preview

It is my dream to one day attend Google I/O. But seeing as I’m from a Third World country where everyday is a practice in cost-cutting, it is very unlikely that I would fulfill that dream anytime soon. I haven’t sat down and computed the actual cost, but thinking about it makes my head spin. Off the top of my head:<div><ul><li>US Visa application = P6500 (~USD150)</li><li>Round trip plane fare ticket = P90,000 (~USD2000, if I’m lucky)</li><li>Google I/O ticket = P21,000 (~USD450, if my research is correct)</li></ul><div>Then of course, there’s food, transportation, accommodations, and pocket money. sigh</div></div><div>
</div>For now, I would have to content myself with watching videos from the sessions. Yesterday, I watched Android SDK tech lead’s Xavier Ducrohet’s session on the Android Development Tools. I had a little interaction with Xavier on the Android developers Google group before, and it is refreshing to finally map a face to the name. I kinda feel bad too, because the few times that I talked to him in the groups, it is to complain about the problems I’m having whenever I update my ADT. Eclipse, the SDK and I have a love-hate relationship with each other. But that’s another story.<div>
</div><div>Anyway, I was watching the session while having lunch which turned out to be a mistake. I was so awestruck by the new tools that I totally forgot to eat and my chicken got cold! I was gushing about the tools, but of course no one can relate as I am the only Android developer in our company. I was applauding and cheering by myself. Oh well.</div><div>
</div><div>Needless to say, I am so impressed with the new version of ADT! (More exclamation marks should follow, but I’m trying to act mature) I used to hate making layout files. I am lightyears away from being artistic, I don’t have a strong background in XML, and I simply find all the available features overwhelming. It’s developer option paralysis, I would think. But the new ADT makes making layouts look like F-U-N! I love how it’s sleeker, seemingly more user-friendly (I’d retain the qualifier until I get to try it out), and more feature-packed. </div><div>
</div><div>Some of the reasons I’m already loving the new ADT:</div><div>1. Smarter extract to include</div><div>The current ADT already has an extract to include feature, but the new version looks smarter. When you choose to extract a layout stub as an include, the ADT looks for the exact same layout in all your other layout files and replaces them with the include tag! Brilliant, I say!</div><div>
</div><div>2. XML editors: attribute auto-complete</div><div>Oh. My. God. I have been ranting about this for so long! There are simply too many attributes available for each type of widget I find it impossible to remember everything! And then of course I have to make sure that I am putting the correct value formats into those attributes! I admit that it terrified me when I was starting out in Android; having this feature saves the newbies from a lot of heartache.</div><div>
</div><div>3. Layout actions toolbar</div><div>Easier to set attributes without having to scroll down a lot then finding out you put in the wrong value and then having to scroll down again. Nifty!</div><div>
</div><div>4. Widget Previews in the palette</div><div>One time I was creating a layout file, I wanted to put an indeterminate progress bar but I want it to use the small progress bar style. But then I lost my list of native Android styles so I have to Google for the correct style that I want (?android:attr/android:progressBarStyleSmall is a little hard to remember).</div><div>
</div><div>Soon I can choose precisely what I want before dragging widgets onto the canvas! The previews plus the attribute autocomplete feature would work wonders, I would guess. And it is wonderful that the previews change with the selected theme!</div><div>
</div><div>5. Support for more widgets</div><div>I used to think that I’m doing an include wrong, because the graphical editor doesn’t show the layout I’m including. It turns out that I have to put it inside a LinearLayout just to have the editor render it properly. But it looks like those days are behind us now!</div><div>
</div><div>Also gone are the days when the ADT cannot render TabHosts! I’m doing my happy dance now!</div><div>
</div><div>6. (Hopefully) easier drag-and-drop</div><div>Dragging-and-dropping in RelativeLayouts is a pain in the ass. The editor simply does not listen to me! But in the new ADT it looks like the new anchors are easier to understand than the current one, with squares all around.</div><div>
</div><div>I haven’t tried dragging a view directly into the outline in the current ADT, but knowing that I would have it soon makes me happy!</div><div>
</div><div><div>7. Custom views available in the GUI editor</div><div>And then Xavier’s team showed us how much they love us by including custom views in the widget palette. I haven’t really used that many custom views, but I guess a lot of developers who customize like crazy would love this feature.</div></div><div>
</div><div><hr width="50%">
</div><div>That being said, I would like to thank Xavier and his team for making sure that us developers continue to love doing what we do by making our jobs a little easier. I would like to think that they take time to listen to our opinions, and they for sure make it easy for us to talk to them. I love how accessible they are, and how very supportive of us. Sending good vibes to you and your team, Xavier!</div><div>
</div><div>I just hope that having this new ADT would not make developers lazy. I wish that we still make sure we understand how the XML file works, that we still read the documentation for the widgets, that we simply do not rely on automation to do the work for us. For starters, make sure you know how to change an EditText to accept passwords, or phone numbers, or only letters without relying on the palette.</div><div>
</div><div>I admit that I screech like a true fan girl when Romain Guy or Xavier answer my tweets. blush
<div>
</div><div>Here’s the session’s video. Gush with me!</div><div>
<div><iframe width="480" height="295" src="http://www.youtube.com/embed/Oq05KqjXTvs?fs=1" frameborder="0" allowfullscreen=""></iframe></div></div></div>

4 min read

Changing a button’s text color

There are times that when changing a button's background color, we also want to change the text's color. There is a method setTextColor(int color) specifically for this purpose. Seems pretty straightforward enough, but it took me a few tries to get it right the first time I tried using it. Documenting it here so that I wouldn't forget.

Here’s a screenshot of my button with its default settings.<div><div style="text-align: center;">
</div></div><div>What I’m trying to do is have the color of the text change to a shade of blue once the button is pressed. But look what I got.</div><div>That is no shade of blue! Far from it! I tried changing the alpha value of the #AARRGGBB value, but I still end up with the same gray shade. It turns out that you cannot pass a direct R.* id to the setTextColor() method.</div><div>
</div><div>Here’s how I finally managed to make it work:</div><div><pre class="brush:java">int newColor = getResources().getColor(R.color.button_new_color);
((Button)view).setTextColor(newColor);</pre>And tada!</div><div></div>

~1 min read

Using CWAC’s EndlessAdapter with a custom adapter

In one of my projects, the app has the potential to display a very, and I mean very, long list. To minimize the loading time of the app, I limit the number of items initially included in the list and then add to it as the user scrolls down.

For this purpose, Mark Murphy’s EndlessAdapter works wonders. I was trying to make it work with a CursorAdapter though, but due to time constraints, I was not able to continue with my experiment.

And then I found out that some of the items in my list are HTML-formatted. Huh. So I have to have a custom adapter to override getView(). I patterned my code on the demo included in the EndlessAdapter project and I was at a loss. Maybe it’s because I just came from a vacation and my mind refuses to work. Hmmm.

To cut a long and arduous journey short, I was able to figure it out. Here’s a sample activity that displays a list of countries from an array. To illustrate using a custom adapter, I add the list item number when setting the item text.

I will discuss the pertinent parts of the code in detail.
<pre class="brush:java">private void init() {
LIST_SIZE = COUNTRIES.length;
for (int i=0; i<=BATCH_SIZE; i++){
countriesSub.add(COUNTRIES[i]);
}

setLastOffset(BATCH_SIZE);
displayList(countriesSub);
}</pre>In this part of the code, I get the first 10 items in the list as the initial contents of the list. Of course, our current list is small and is peanuts for ListView. This is just to illustrate my point. In my app, I initially load 2,000 items. I also make sure to remember where I am in my source list. In my case, it is the offset in the original array. This might also be a row in the DB, or the ID in an RSS feed.

The ArrayList countriesSub holds the items that are currently in my list. As the user goes through the list, this array will grow.

To display my list, I set an instance of DemoAdapter as my list adapter. DemoAdapter inherits from EndlessAdapter and in its constructor, I give it an instance of my custom ArrayAdapter.
<pre class="brush:java">@Override
public View getView(int position, View convertView, ViewGroup parent) {

ViewHolder holder;

if(convertView==null){
LayoutInflater inflater = (LayoutInflater)
EndlessCustom.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(android.R.layout.simple_list_item_1, null);
holder = new ViewHolder();
holder.word = (TextView)convertView.findViewById(android.R.id.text1);

convertView.setTag(holder);
} else{
holder=(ViewHolder)convertView.getTag();
}

holder.word.setText(String.valueOf(position+1) + “. “ + countriesSub.get(position));
return convertView;
}</pre>
The important part in my custom ArrayAdapter is the getView() method. In this method, I tell the adapter to not simply display the .toString() value of the item, but to add a number before it.

Notice that I use the ViewHolder pattern as illustrated in the Android Developers site.
<pre class="brush:java; tab-size: 5">DemoAdapter() {
super(new CustomArrayAdapter(EndlessCustom.this,
android.R.layout.simple_list_item_1, android.R.id.text1, countriesSub));

rotate=new RotateAnimation(0f, 360f, Animation.RELATIVE_TO_SELF,
0.5f, Animation.RELATIVE_TO_SELF,
0.5f);
rotate.setDuration(600);
rotate.setRepeatMode(Animation.RESTART);
rotate.setRepeatCount(Animation.INFINITE);
}</pre>
So now we come to the EndlessAdapter part. In the constructor, I pass into it an instance of my custom ArrayAdapter, indicating the source of the list, the layout for each row, and the TextView ID from the layout. I also instantiated the animation that will be used while the list is loading the additional items.
<pre class="brush:java">@Override
protected boolean cacheInBackground() {
tempList.clear();
int lastOffset = getLastOffset();
if(lastOffset < LIST_SIZE){
int limit = lastOffset + BATCH_SIZE;
for(int i=(lastOffset+1); (i<=limit && i<LIST_SIZE); i++){
tempList.add(COUNTRIES[i]);
}

setLastOffset(limit);

if(limit<LIST_SIZE){
return true;
} else {
return false;
}
} else {
return false;
}
}</pre>We have to override cacheInBackground() for EndlessAdapter to work. Here we do the heavy lifting like querying the server, reading from the DB, etc. Here, I load the next 10 items from the original list and put them in a temporary ArrayList. I also check whether I have loaded all the data, and if so, tell the EndlessAdapter to not show the extra row at the bottom. I do this by returning false from the method.
<pre class="brush:java; tab-size: 5">@Override
protected void appendCachedData() {
@SuppressWarnings(“unchecked”)
ArrayAdapter<String> arrAdapterNew = (ArrayAdapter<String>)getWrappedAdapter();

int listLen = tempList.size();
for(int i=0; i<listLen; i++){
arrAdapterNew.add(tempList.get(i));
}
}</pre>Finally, I add the newly retrieved rows to my current list. And that’s it.

The complete Java file for this activity follows:
<pre class="brush:java; collapse: true">package com.test.example;

import java.util.ArrayList;
import java.util.List;

import android.app.ListActivity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ArrayAdapter;
import android.widget.TextView;

import com.commonsware.cwac.endless.EndlessAdapter;

public class EndlessCustom extends ListActivity {

static int LIST_SIZE;
private int mLastOffset = 0;

static final int BATCH_SIZE = 10;

ArrayList<String> countriesSub = new ArrayList<String>();

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.lib_activity_dictionary);
init();
}

private void init() {
LIST_SIZE = COUNTRIES.length;
for (int i=0; i<=BATCH_SIZE; i++){
countriesSub.add(COUNTRIES[i]);
}
setLastOffset(BATCH_SIZE);
displayList(countriesSub);
}

private void setLastOffset(int i) {
mLastOffset = i;
}

private int getLastOffset(){
return mLastOffset;
}

private void displayList(ArrayList<String> countriesSub) {
setListAdapter(new DemoAdapter());
}

private class CustomArrayAdapter extends ArrayAdapter<String>{

public CustomArrayAdapter(Context context, int resource,
int textViewResourceId, List<String> objects) {
super(context, resource, textViewResourceId, objects);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {

ViewHolder holder;

if(convertView==null){
LayoutInflater inflater = (LayoutInflater)
EndlessCustom.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(android.R.layout.simple_list_item_1, null);
holder = new ViewHolder();
holder.word = (TextView)convertView.findViewById(android.R.id.text1);

convertView.setTag(holder);
} else{
holder=(ViewHolder)convertView.getTag();
}

holder.word.setText(String.valueOf(position+1) + “. “ + countriesSub.get(position));
return convertView;
}

public class ViewHolder{
public TextView word;
}
}

class DemoAdapter extends EndlessAdapter {
private RotateAnimation rotate=null;
ArrayList<String> tempList = new ArrayList<String>();

DemoAdapter() {
super(new CustomArrayAdapter(EndlessCustom.this,
android.R.layout.simple_list_item_1, android.R.id.text1, countriesSub));

rotate=new RotateAnimation(0f, 360f, Animation.RELATIVE_TO_SELF,
0.5f, Animation.RELATIVE_TO_SELF,
0.5f);
rotate.setDuration(600);
rotate.setRepeatMode(Animation.RESTART);
rotate.setRepeatCount(Animation.INFINITE);
}

@Override
protected View getPendingView(ViewGroup parent) {
View row=getLayoutInflater().inflate(R.layout.row, null);

View child=row.findViewById(android.R.id.text1);
child.setVisibility(View.GONE);
child=row.findViewById(R.id.throbber);
child.setVisibility(View.VISIBLE);
child.startAnimation(rotate);

return(row);
}

@Override
protected boolean cacheInBackground() {
tempList.clear();
int lastOffset = getLastOffset();
if(lastOffset < LIST_SIZE){
int limit = lastOffset + BATCH_SIZE;
for(int i=(lastOffset+1); (i<=limit && i<LIST_SIZE); i++){
tempList.add(COUNTRIES[i]);
}
setLastOffset(limit);

if(limit<LIST_SIZE){
return true;
} else {
return false;
}
} else {
return false;
}
}


@Override
protected void appendCachedData() {

@SuppressWarnings(“unchecked”)
ArrayAdapter<String> arrAdapterNew = (ArrayAdapter<String>)getWrappedAdapter();

int listLen = tempList.size();
for(int i=0; i<listLen; i++){
arrAdapterNew.add(tempList.get(i));
}
}
}


static final String[] COUNTRIES = new String[] {
“Afghanistan”, “Albania”, “Algeria”, “American Samoa”, “Andorra”,
“Angola”, “Anguilla”, “Antarctica”, “Antigua and Barbuda”, “Argentina”,
“Armenia”, “Aruba”, “Australia”, “Austria”, “Azerbaijan”,
“Bahrain”, “Bangladesh”, “Barbados”, “Belarus”, “Belgium”,
“Belize”, “Benin”, “Bermuda”, “Bhutan”, “Bolivia”,
“Bosnia and Herzegovina”, “Botswana”, “Bouvet Island”, “Brazil”, “British Indian Ocean Territory”,
“British Virgin Islands”, “Brunei”, “Bulgaria”, “Burkina Faso”, “Burundi”,
“Cote d’Ivoire”, “Cambodia”, “Cameroon”, “Canada”, “Cape Verde”,
“Cayman Islands”, “Central African Republic”, “Chad”, “Chile”, “China”,
“Christmas Island”, “Cocos (Keeling) Islands”, “Colombia”, “Comoros”, “Congo”,
“Cook Islands”, “Costa Rica”, “Croatia”, “Cuba”, “Cyprus”, “Czech Republic”,
“Democratic Republic of the Congo”, “Denmark”, “Djibouti”, “Dominica”, “Dominican Republic”,
“East Timor”, “Ecuador”, “Egypt”, “El Salvador”, “Equatorial Guinea”, “Eritrea”,
“Estonia”, “Ethiopia”, “Faeroe Islands”, “Falkland Islands”, “Fiji”, “Finland”,
“Former Yugoslav Republic of Macedonia”, “France”, “French Guiana”, “French Polynesia”,
“French Southern Territories”, “Gabon”, “Georgia”, “Germany”, “Ghana”, “Gibraltar”,
“Greece”, “Greenland”, “Grenada”, “Guadeloupe”, “Guam”, “Guatemala”, “Guinea”, “Guinea-Bissau”,
“Guyana”, “Haiti”, “Heard Island and McDonald Islands”, “Honduras”, “Hong Kong”, “Hungary”,
“Iceland”, “India”, “Indonesia”, “Iran”, “Iraq”, “Ireland”, “Israel”, “Italy”, “Jamaica”,
“Japan”, “Jordan”, “Kazakhstan”, “Kenya”, “Kiribati”, “Kuwait”, “Kyrgyzstan”, “Laos”,
“Latvia”, “Lebanon”, “Lesotho”, “Liberia”, “Libya”, “Liechtenstein”, “Lithuania”, “Luxembourg”,
“Macau”, “Madagascar”, “Malawi”, “Malaysia”, “Maldives”, “Mali”, “Malta”, “Marshall Islands”,
“Martinique”, “Mauritania”, “Mauritius”, “Mayotte”, “Mexico”, “Micronesia”, “Moldova”,
“Monaco”, “Mongolia”, “Montserrat”, “Morocco”, “Mozambique”, “Myanmar”, “Namibia”,
“Nauru”, “Nepal”, “Netherlands”, “Netherlands Antilles”, “New Caledonia”, “New Zealand”,
“Nicaragua”, “Niger”, “Nigeria”, “Niue”, “Norfolk Island”, “North Korea”, “Northern Marianas”,
“Norway”, “Oman”, “Pakistan”, “Palau”, “Panama”, “Papua New Guinea”, “Paraguay”, “Peru”,
“Philippines”, “Pitcairn Islands”, “Poland”, “Portugal”, “Puerto Rico”, “Qatar”,
“Reunion”, “Romania”, “Russia”, “Rwanda”, “Sqo Tome and Principe”, “Saint Helena”,
“Saint Kitts and Nevis”, “Saint Lucia”, “Saint Pierre and Miquelon”,
“Saint Vincent and the Grenadines”, “Samoa”, “San Marino”, “Saudi Arabia”, “Senegal”,
“Seychelles”, “Sierra Leone”, “Singapore”, “Slovakia”, “Slovenia”, “Solomon Islands”,
“Somalia”, “South Africa”, “South Georgia and the South Sandwich Islands”, “South Korea”,
“Spain”, “Sri Lanka”, “Sudan”, “Suriname”, “Svalbard and Jan Mayen”, “Swaziland”, “Sweden”,
“Switzerland”, “Syria”, “Taiwan”, “Tajikistan”, “Tanzania”, “Thailand”, “The Bahamas”,
“The Gambia”, “Togo”, “Tokelau”, “Tonga”, “Trinidad and Tobago”, “Tunisia”, “Turkey”,
“Turkmenistan”, “Turks and Caicos Islands”, “Tuvalu”, “Virgin Islands”, “Uganda”,
“Ukraine”, “United Arab Emirates”, “United Kingdom”,
“United States”, “United States Minor Outlying Islands”, “Uruguay”, “Uzbekistan”,
“Vanuatu”, “Vatican City”, “Venezuela”, “Vietnam”, “Wallis and Futuna”, “Western Sahara”,
“Yemen”, “Yugoslavia”, “Zambia”, “Zimbabwe”
};
}</pre>
If you know of a more efficient way to do this, please do not hesitate to let me know! :)

6 min read

My EditText is cut off by the on-screen keyboard!

With clients demanding left and right that my app should look like an iPhone app, I tend to be unappreciative of the way Android natively handles UI interactions and such. Notice how the screen automagically scrolls up when you click on an EditText? It turns out that in iPhone development, the developer does this manually (indicate how much the view should scroll when the on-screen keyboard appears, then scroll it back down afterwards). HA!

But even magic fails sometimes. Has this ever happened to you?
<img style=”display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 134px; height: 200px;” src=”http://3.bp.blogspot.com/-JftkETrkB2c/TZlrHJOji5I/AAAAAAAAA4I/cX4jSBJRiGY/s200/edittext_hidden.png” border=”0” alt=”“id=”BLOGGER_PHOTO_ID_5591618182837406610” />
The bottommost EditText is cut off. And we don’t want that!

So what do we do? Do we programmatically scroll the view up? I don’t want to do that! It turns out that we can just wrap the whole view in a ScrollView and it will scroll up properly!
<img style=”display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 134px; height: 200px;” src=”http://1.bp.blogspot.com/-J2UCMLF_E3k/TZlrk_lKBdI/AAAAAAAAA4Q/c7G321XalBo/s200/edittext_shown.png” border=”0” alt=”“id=”BLOGGER_PHOTO_ID_5591618695643923922” />

The only downside to this is that you might want to hide the scrollbar when the view moves up to accommodate the on-screen keyboard. And to do that, I was trying to set the android:scrollbars=”none” attribute but for one reason or another the scrollbar is still being drawn. To make the scrollbar disappear, we can do it from code as such:<pre class="brush:java">((ScrollView)findViewById(R.id.my_scrollview))
.setVerticalScrollBarEnabled(false);</pre>And we’re done!

~1 min read

Using a custom font in WebView

In one of my projects, I needed to display some special characters that the Android OS by itself cannot seem to render. I figured that I would need to provide a custom font that includes the characters that I needed.

If I was using a TextView, I could use TextView#setTypeFace. But I was using a WebView and I feared that things would be more complicated than that. So how do I do this?

Here’s how we can make it work.

Step 1: We would need to have our font face included in our project’s /assets folder. So look for a TTF file that you can use freely, make sure the author/creator allows you to re-distribute it!

Step 2: Edit your HTML file to include some CSS stuff, just so the WebView would know what font you want to use. Here’s a sample file: <pre class="brush:xml"><html><head><link href=”YourCssFile.css” rel=”stylesheet” type=”text/css” /></head><body><span class=”phon”>This string contains special characters: əˈpåstrəfi </span></body></html></pre>Make sure that the href references are correct. In this case, my CSS file, HTML file and font file are in the same folder.

Step 3: Define your CSS file. In this case, our YourCssFile.css would be:<pre class="brush:css">@font-face {
font-family: “My font”;
src: url(‘MyFontFile.TTF’);
}

.phon, .unicode {
display: inline;
font-family: ‘My font’, Verdana, sans-serif;
font-size: 14pt;
font-weight: 500;
font-style:normal;
color: black;
}</pre>Step 4: Load the file in your WebView! You can use <pre class="brush:java">WebView webView = (WebView) findViewById(R.id.myWebview);
webView.loadUrl(“path/to/html/file”);</pre> or <pre class="brush:java">webView.loadDataWithBaseURL(“file:///android_asset/”,
article, “text/html”, Encoding.UTF_8.toString(), null);</pre>If you will use the second option, your article variable would contain the HTML string. Just make sure that you escape your quotation marks with a backslash ().
<blockquote><blockquote>IMPORTANT NOTE: This feature seems to be broken in Android 2.0 and 2.1, as reported here.</blockquote></blockquote>

1 min read

It never ends!

Been ultra super busy the past few weeks. Also learning a lot of new things. And renewing my battle with orientation change, AsyncTasks and dialog boxes.

So, I have been thinking of what to document here next. Here are my choices:
- Loading HTML pages stored in the SD card in a WebView
- Managing your SharedPreferences
- Using a ViewStub

I had a long list in my mind yesterday, but I managed to not write it down. facepalm

~1 min read

stealth ninja mode on

Over the past couple of weeks, this blog has been getting unusually high traffic. Which means I get more than one hit per week.

So if it isn’t too much to ask, can I please know how you got here, and what you were looking for. And please please let me know if I said anything wrong, or if you know of a better/easier/more optimized way to do the stuff I’m trying to talk about. Thanks! :)

~1 min read

That damn seekbar thumb

If you have ever needed to use a SeekBar, you definitely would have noticed how hard it is to move the slider (aka thumb) when it is set to the minimum or maximum value. The slider tends to be cut in half, and fitting your finger into it to press it becomes a test of patience.<div></div>
See how small the slider becomes when it reaches the far ends of the SeekBar? Crazy!

Luckily, I found a way (just today!) to move the slider just a little tiny bit to make it easier to press. Apparently, there is a method called setThumbOffset() that allows us to nudge the slider by a number of pixels.

It’s pretty easy to use, aside from the fact that it accepts pixels and not dip measurements. Anyway, here’s how to do it:<pre class="brush:java">int pixels = convertDipToPixels(8f);
SeekBar mySeekBar = (SeekBar) findViewById(R.id.quiz_settings_seekbar);
mySeekBar.setOnSeekBarChangeListener(mySeekBarListener);
mySeekBar.setThumbOffset(pixels);</pre>I convert dip measurements to pixels to better manage the growing number of resolutions of screen sizes present. Here’s the code to do that:<pre class="brush:java">private int convertDipToPixels(float dip) {
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
float density = metrics.density;
return (int)(dip * density);
}</pre>Aaaaaaaand this is now how our slider looks:Applause! Confetti! Applause!

~1 min read

Enabling/disabling menu items on the fly

In one of my applications, I want to disable some menu entries if the database is not valid or is not present at all. To do that, I make use of the onPrepareOptionsMenu() API.

Let’s say the user is on the Quiz activity. When the user presses the MENU button, the Quiz item should not be there anymore; and until the application validates the presence of a database, I want the Books item to be unclickable.

So I inflate my menu as usual from the XML file:<pre class="brush:java">@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.my_menu, menu);

removeSomeItems(menu);
return true;
}

private void removeSomeItems(Menu menu){
MenuItem books = menu.findItem(R.id.menu_books);
books.setEnabled(false); // I want this item to be available eventually

menu.removeItem(R.id.menu_quiz); // I want this item to be unavailable for this activity
}</pre>
You have to remember though, that this method is called only ONCE, the very first time the user opens the menu.

The activity does its business as usual. When I am done checking if the database is there already, I want the user to be able to use the Books item. I have a boolean field that I set when the database is validated, and so now I can enable the menu.<pre class="brush:java">@Override
public boolean onPrepareOptionsMenu (Menu menu){
super.onPrepareOptionsMenu(menu);

if(mIsDbValid){
MenuItem books = menu.findItem(R.id.menu_books);
books.setEnabled(true);
}

return true;
}</pre>

You can change the contents of the menu as often as you want using onPrepareOptionsMenu() because that method is called each and every time the user presses the MENU button.

And that’s it!

1 min read

Just wondering

You know those postscripts that Google engineers have on their posts in forums? I wonder if they have a sort of “standards body” that came up with it. They all sound like this:<blockquote>Note: please don’t send private questions to me, as I don’t have time to provide private support. All such questions should be posted on public
forums, where I and others can see and answer them </blockquote>

I wonder why I wonder about these things.

~1 min read