Android Developer Tips & Tricks

28 Apr

This blog post is aimed at any Android developer who would like to learn a few things that you don’t necessarily learn through straight coding or Google searches about things you’re stuck with. These hints, tips and tricks are just some things I have discovered that I wish I had known about from the beginning to make my life easier.

Here’s the shortlist, scroll down to see more details.

#1 – Transparent colours
#2 – dp & sp as unit dimensions
#3 – Do the heavy lifting in the background thread
#4 – Show a loading/progress bar on app creation
#5 – Image caching
#6 – Don’t refresh on orientation change
#7 – Block orientation change
#8 – Refresh activity
#9 – Learn the Activity Lifecycle

I will add to the list as I remember more things or make more discoveries.

#1 – Transparent Colours

Android allows (in most places) for an extra two digits in colour codes to give colours a transparency value!
The extra two digits go at the front, FF means fully visible, 00 means invisible. Values in between can also be used to get varying levels of transparency.

Examples:

// Fully visible, grey colour
jsaOverviewLayout.setBackgroundColor(0xFF2c3033);
/*
Gradient drawable that is;
	-Fully visible white [left] (0xFFFFFFFF)
	-Fully visible orange [middle] (0xFFFF6400)
	-Invisible black [right] (0x00000000) (colour here is irrelevant)
*/
GradientDrawable divider = new GradientDrawable(
Orientation.LEFT_RIGHT, new int[] {0xFFFFFFFF, 0xFFFF6400, 0x00000000});
<LinearLayout
    	android:layout_width="fill_parent"
    	android:layout_height="wrap_content"
    	android:background="#FFFFFFFF"
    	android:orientation="horizontal"
    	android:id="@+id/item_viewer_header_layout" >

#2 – dp & sp As Unit Dimensions

You may be wondering what they mean and when to use them!
Basically: ‘dp’ units should be used for layout items, while ‘sp’ should be used for font sizes!

dp and sp unit’s were designed for xml, but there are workarounds to get them working programmatically in the code.

dp = Density-Independent Pixels (can also use ‘dip’)
dp is a unit of measurement based on screen density, to allow for different screen sizes for all the Android enabled devices out there. It gives an abstract view, instead of hard-coding a layout as a set amount of pixels (px), dp allows Android to stretch/shrink the layout item based on the screen size/density.

sp = Scale-Independent Pixels
sp does the density scaling as well, but also scales the font based on user preference.

I found myself trying to always shape layouts dynamically with FILL_PARENT and WRAP_CONTENT, it was quite hard for more complex layout. Then I found the magic of using dp.

Example:

<TextView
	android:id="@+id/feed_page_title"
	android:layout_width="50dp"
	android:layout_height="25dp"
	android:text="Yippie Kai Yay"
	android:textSize="16sp"
	android:textColor="#00b000"
	android:typeface="serif" />

Bonus: How to use dp/sp programmatically instead of just in xml
There are lots of conversion methods available here
http://stackoverflow.com/questions/4605527/converting-pixels-to-dp-in-android

MORE CONTENT COMING SOON!

Working Vertical SeekBar for Android

20 Apr

More than likely you’ve come here wanting source code for a fully working Vertical SeekBar. I’ve made this quick tutorial due to frustration over the lack of working solutions found on Google and StackOverflow – They mostly worked, but there was always something wrong with each implementation; the progress didn’t update correctly, the bar didn’t draw correctly, the thumb didn’t unhighlight, or maybe it just didn’t look right. This solution seems to solve all of this.
Vertical SeekBar

Here is the important parts of the VerticalSeekBar class. I merged a few solutions I found to make this. The goal was to make VerticalSeekBar behave exactly like a normal horizontal SeekBar.

public class VerticalSeekBar extends SeekBar {

	public VerticalSeekBar(Context context) {
		super(context);
	}

	public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	public VerticalSeekBar(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(h, w, oldh, oldw);
	}

	@Override
	protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(heightMeasureSpec, widthMeasureSpec);
		setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
	}

	protected void onDraw(Canvas c) {
		c.rotate(-90);
		c.translate(-getHeight(), 0);

		super.onDraw(c);
	}

	private OnSeekBarChangeListener onChangeListener;
	@Override
	public void setOnSeekBarChangeListener(OnSeekBarChangeListener onChangeListener){
		this.onChangeListener = onChangeListener;
	}

	private int lastProgress = 0;
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		if (!isEnabled()) {
			return false;
		}

		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			onChangeListener.onStartTrackingTouch(this);
			setPressed(true);
			setSelected(true);
			break;
		case MotionEvent.ACTION_MOVE:
			super.onTouchEvent(event);
			int progress = getMax() - (int) (getMax() * event.getY() / getHeight());
			
			// Ensure progress stays within boundaries
			if(progress < 0) {progress = 0;}
			if(progress > getMax()) {progress = getMax();}
			setProgress(progress);	// Draw progress
			if(progress != lastProgress) {
				// Only enact listener if the progress has actually changed
				lastProgress = progress;
				onChangeListener.onProgressChanged(this, progress, true);
			}
			
			onSizeChanged(getWidth(), getHeight() , 0, 0);
			setPressed(true);
			setSelected(true);
			break;
		case MotionEvent.ACTION_UP:
			onChangeListener.onStopTrackingTouch(this);
			setPressed(false);
			setSelected(false);
			break;
		case MotionEvent.ACTION_CANCEL:
			super.onTouchEvent(event);
			setPressed(false);
			setSelected(false);
			break;
		}
		return true;
	}

	public synchronized void setProgressAndThumb(int progress) {
		setProgress(progress);
		onSizeChanged(getWidth(), getHeight() , 0, 0);
		if(progress != lastProgress) {
			// Only enact listener if the progress has actually changed
			lastProgress = progress;
			onChangeListener.onProgressChanged(this, progress, true);
		}
	}

	public synchronized void setMaximum(int maximum) {
		setMax(maximum);
	}

	public synchronized int getMaximum() {
		return getMax();
	}
}

I then inserted the VerticalSeekBar inside a LinearLayout like so:

<LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <com.safetyculture.jsadroidtablet.VerticalSeekBar
        android:id="@+id/calculatorVerticalSeekBar"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_gravity="bottom"
        android:max="4"
        android:progress="2" />

</LinearLayout>

NOTE: In this implementation you must set an OnSeekBarChangeListener for the VerticalSeekBar before using it in the app, otherwise interacting with the VerticalSeekBar will produce NullPointerException.

NOTE2: If you wish to change the progress manually/programmatically rather than through the UI Motion Events, you will need to call setProgressAndThumb(progress), otherwise the thumb doesn’t update correctly.

Follow

Get every new post delivered to your Inbox.