Saturday, 28 May 2016

Android UI Design

Android UI Design
Key Features
  • Take an initial idea for an Android app and develop it into a detailed plan, supported by sketches and wireframes
  • Provide a better experience for your users by following best practices and the new material design principles
  • Work more efficiently and save time by testing your ideas at an early stage by building a prototype
Book Description
Great design is one of the key drivers in the adoption of new applications, yet unfortunately design considerations are often neglected in the face of "will it work," "can we make it quicker," or "can we get more people using it"?

This book seeks to redress this balance by showing you how to get your PM to start treating the design phase of your project seriously. This book is focused entirely on the development of UI features, and you'll be able to practically implementing the design practices that we extol throughout the book.

Starting by briefly outlining some of the factors you need to keep in mind when building a UI, you'll learn the concepts of Android User Interface from scratch. We then move on to formulate a plan on how to implement these concepts in various applications. We will deep dive into how UI features are implemented in real-world applications where UIs are complex and dynamic.

This book offers near complete coverage of UI-specific content including, views, fragments, the wireframing process, and how to add in splash screens-everything you need to make professional standard UIs for modern applications. It will then cover material design and show you how to implement Google's design aesthetic in a practical manner. Finally, it ensures the best possible user experience by analyzing the UI using various tools, and then addressing any problems they uncover.

By the end of the book, you'll be able to leverage the concepts of Android User Interface in your applications in order to attract new customers.

What you will learn
  • Develop a user interface that adheres to all the core material design principles
  • Transform your initial app idea into a concrete and detailed plan
  • Add Views, ViewGroups, layouts, and common UI components to your own Android projects
  • Use fragments and various strategies to gather user input
  • Create a new Android Studio project and develop it into a prototype
  • Identify and solve problems with your app's UI to deliver a better user experience
About the Author
Jessica Thornsby studied poetry, prose, and script writing at Bolton University before discovering the world of open source and technical writing, and has never looked back since. Today, she is a technical writer and full-time Android enthusiast residing in sunny Sheffield, England.

She enjoys writing about rooting and flashing mobile devices, Java, Eclipse, and all things Android and Google. When not wordsmithing about technology and obsessing about the latest Android developments, she keeps a blog about her local food scene, and writes about looking after exotic pets. On the rare occasions that she's dragged away from her computer, she enjoys beer gardens, curry houses, the Great British seaside, scary movies, and spending lots of time with her house rabbits and chinchillas.

Monday, 23 May 2016

Android client to send message to Java server on Raspberry Pi


It's a Java client and server exercise in my another blogspot HelloraspberryPi. The client connect to server and send something, the server simple print the received message and close.

It's Android version of the client, to connect to server and send something.


MainActivity.java
package com.blogspot.android_er.androidclient;

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class MainActivity extends AppCompatActivity {

EditText editTextAddress, editTextPort, editTextMsg;
Button buttonConnect;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editTextAddress = (EditText) findViewById(R.id.address);
editTextPort = (EditText) findViewById(R.id.port);
editTextMsg = (EditText) findViewById(R.id.msgtosend);
buttonConnect = (Button) findViewById(R.id.connect);

buttonConnect.setOnClickListener(buttonConnectOnClickListener);
}

View.OnClickListener buttonConnectOnClickListener =
new View.OnClickListener() {

@Override
public void onClick(View arg0) {
MyClientTask myClientTask = new MyClientTask(
editTextAddress.getText().toString(),
Integer.parseInt(editTextPort.getText().toString()));
myClientTask.execute(editTextMsg.getText().toString());
}
};

public class MyClientTask extends AsyncTask<String, Void, Void> {

String dstAddress;
int dstPort;
String response;

MyClientTask(String addr, int port) {
dstAddress = addr;
dstPort = port;
}

@Override
protected Void doInBackground(String... params) {
try {
Socket socket = new Socket(dstAddress, dstPort);

OutputStream outputStream = socket.getOutputStream();
PrintStream printStream = new PrintStream(outputStream);
printStream.print(params[0]);

socket.close();

} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}

}
}


layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:orientation="vertical"
tools:context="com.blogspot.android_er.androidclient.MainActivity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://android-er.blogspot.com/"
android:textStyle="bold" />
<EditText
android:id="@+id/address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="dstAddress" />
<EditText
android:id="@+id/port"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="dstPort" />
<EditText
android:id="@+id/msgtosend"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="msg to send..." />
<Button
android:id="@+id/connect"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Connect and send..."/>

</LinearLayout>


uses-permission of "android.permission.INTERNET" is needed in AndroidManifest.xml

The Java code of the server side, refer to "Java Network exercise: client and server - client send something to server".

The next example "Android client example 2, communicate with Java Server run on Raspberry Pi" show how Android Client connect to Java Server (run on raspberry Pi) and keep connected, and send to and receive from Server.

Parse blogspot JSON feed: detect item clicked to open in browser using CustomTabsIntent


Last post show a example to "Download and parse blogspot JSON feed". In this post, OnItemClickListener is added to the ListView; for user clicking to open the url in browser, using "Simplest way to open browser using CustomTabsIntent.Builder".


To use CustomTabsIntent.Builder() in your project, you have to edit appbuild.gradle to add dependencies of compile 'com.android.support:customtabs:23.0.0'.

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:customtabs:23.0.0'
}


Edit MainActivity.java to add OnItemClickListener.
package com.blogspot.android_er.androidparsejson;

import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.customtabs.CustomTabsIntent;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

Button btnLoadFeed;
TextView textViewFeedUrl;
ListView listViewFeed;

List<FeedItem> listFeedItems;
ListAdapter adapterFeed;

String myFeed = "http://android-er.blogspot.com/feeds/posts/default?alt=json";
//String myFeed = "http://arduino-er.blogspot.com/feeds/posts/default?alt=json";
//String myFeed = "http://helloraspberrypi.blogspot.com/feeds/posts/default?alt=json";
//String myFeed = "http://photo-er.blogspot.com/feeds/posts/default?alt=json";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnLoadFeed = (Button)findViewById(R.id.loadfeed);
textViewFeedUrl = (TextView)findViewById(R.id.feedurl);
listViewFeed = (ListView)findViewById(R.id.listviewfeed);

listFeedItems = new ArrayList<>();
adapterFeed = new ArrayAdapter<FeedItem>(
this, android.R.layout.simple_list_item_1, listFeedItems);
listViewFeed.setAdapter(adapterFeed);
listViewFeed.setOnItemClickListener(listViewFeedOnItemClickListener);

btnLoadFeed.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
textViewFeedUrl.setText(myFeed);
new JsonTask(listFeedItems, listViewFeed).execute(myFeed);
}
});
}

AdapterView.OnItemClickListener listViewFeedOnItemClickListener =
new AdapterView.OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
FeedItem clickedFeedItem = (FeedItem) parent.getItemAtPosition(position);
String url = clickedFeedItem.getUrl();
Uri uri = Uri.parse(url);
new CustomTabsIntent.Builder()
.build()
.launchUrl(MainActivity.this, uri);
}
};


/*
JsonTask:
AsyncTask to download and parse JSON Feed of blogspot in background
*/
private class JsonTask extends AsyncTask<String, FeedItem, String> {

List<FeedItem> jsonTaskList;
ListView jsonTaskListView;

public JsonTask(List<FeedItem> targetList, ListView targetListView) {
super();
jsonTaskList = targetList;
jsonTaskListView = targetListView;
}

@Override
protected void onPreExecute() {
super.onPreExecute();
jsonTaskList.clear();
jsonTaskListView.invalidateViews();
}

@Override
protected String doInBackground(String... params) {

try {
final String queryResult = sendQuery(params[0]);
parseQueryResult(queryResult);
} catch (IOException e) {
e.printStackTrace();

final String eString = e.toString();
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,
eString,
Toast.LENGTH_LONG).show();
}
});

} catch (JSONException e) {
e.printStackTrace();

final String eString = e.toString();
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,
eString,
Toast.LENGTH_LONG).show();
}
});
}
return null;
}

@Override
protected void onProgressUpdate(FeedItem... values) {
FeedItem newItem = values[0];
jsonTaskList.add(newItem);
jsonTaskListView.invalidateViews();
}

private String sendQuery(String query) throws IOException {
String queryReturn = "";
URL queryURL = new URL(query);

HttpURLConnection httpURLConnection = (HttpURLConnection)queryURL.openConnection();

if(httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK){
InputStreamReader inputStreamReader =
new InputStreamReader(httpURLConnection.getInputStream());
BufferedReader bufferedReader = new BufferedReader(
inputStreamReader, 8192);
String line = null;
while((line = bufferedReader.readLine()) != null){
queryReturn += line;
}

bufferedReader.close();
}


return queryReturn;
}

private void parseQueryResult(String json) throws JSONException {
JSONObject jsonObject = new JSONObject(json);
final JSONObject jsonObject_feed = jsonObject.getJSONObject("feed");
final JSONArray jsonArray_entry = jsonObject_feed.getJSONArray("entry");

runOnUiThread(new Runnable() {
@Override
public void run() {
if(jsonArray_entry == null){
Toast.makeText(MainActivity.this,
"jsonArray_entry == null",
Toast.LENGTH_LONG).show();
}else{
Toast.makeText(MainActivity.this,
String.valueOf(jsonArray_entry.length()),
Toast.LENGTH_LONG).show();
for(int i=0; i<jsonArray_entry.length(); i++){
try {
JSONObject thisEntry = (JSONObject) jsonArray_entry.get(i);
JSONObject thisEntryTitle = thisEntry.getJSONObject("title");
String thisEntryTitleString = thisEntryTitle.getString("$t");

JSONArray jsonArray_EntryLink = thisEntry.getJSONArray("link");

//search for the link element with rel="alternate"
//I assume it's one and only one element with rel="alternate",
//and its href hold the link to the page
for(int j=0; j<jsonArray_EntryLink.length(); j++){
JSONObject thisLink = (JSONObject) jsonArray_EntryLink.get(j);
try{
String thisLinkRel = thisLink.getString("rel");
if(thisLinkRel.equals("alternate")){
try{
String thisLinkHref = thisLink.getString("href");
FeedItem thisElement = new FeedItem(
thisEntryTitleString.toString(),
thisLinkHref);
publishProgress(thisElement);
break;
}catch (JSONException e){
//no such mapping exists
}
}
}catch (JSONException e){
//no such mapping exists
}

}

} catch (JSONException e) {
e.printStackTrace();
}

}
}

}
});
}
}
}


Other files, FeedItem.java, activity_main.xml and AndroidManifest.xml, refer last post.

Saturday, 21 May 2016

Simplest way to open browser using CustomTabsIntent.Builder


This post show the simplest way to open browser using CustomTabsIntent.Builder.


To use CustomTabsIntent.Builder, edit app/build.gradle to add dependencies of compile 'com.android.support:customtabs:23.0.0'.


MainActivity.java
package com.blogspot.android_er.androidcustomtabsintent;

import android.net.Uri;
import android.os.Bundle;
import android.support.customtabs.CustomTabsIntent;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

Button btnLaunch;
Uri uriMyBlog = Uri.parse("http://android-er.blogspot.com");

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnLaunch = (Button)findViewById(R.id.launch);
btnLaunch.setOnClickListener(btnLaunchOnClickListener);
}

View.OnClickListener btnLaunchOnClickListener = new View.OnClickListener(){

@Override
public void onClick(View v) {
new CustomTabsIntent.Builder()
.build()
.launchUrl(MainActivity.this, uriMyBlog);

}
};
}


activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:orientation="vertical"
tools:context="com.blogspot.android_er.androidcustomtabsintent.MainActivity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://android-er.blogspot.com/"
android:textStyle="bold" />

<Button
android:id="@+id/launch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Launch"/>
</LinearLayout>


Thursday, 19 May 2016

SwipeRefreshLayout, refresh in background thread


Last post show a basic example of SwipeRefreshLayout. But, normally refreshing involve some longtime task; such as loading data from Internet. This example show how to refresh with longtime task in background thread.


MainActivity.java
package com.blogspot.android_er.androidswiperefresh;

import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.Toast;

import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class MainActivity extends AppCompatActivity {

SwipeRefreshLayout swipeRefreshLayout;
ListView swipeList;

List<String> myList;
ListAdapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

swipeRefreshLayout = (SwipeRefreshLayout)findViewById(R.id.swiperefreshlayout);
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
refresh();
}
});

swipeList = (ListView)findViewById(R.id.swipelist);

myList = new ArrayList<>();
adapter = new ArrayAdapter<String>(
this, android.R.layout.simple_list_item_1, myList);
swipeList.setAdapter(adapter);

swipeList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String item = (String) parent.getItemAtPosition(position);
Toast.makeText(MainActivity.this, item, Toast.LENGTH_LONG).show();
}
});
}

private void refresh(){

final int pos = myList.size();
myList.add(pos, "Refreshing...");
swipeList.invalidateViews();
swipeRefreshLayout.setRefreshing(true);

//refresh long-time task in background thread
new Thread(new Runnable() {
@Override
public void run() {
try {
//dummy delay for 2 second
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}

//update ui on UI thread
runOnUiThread(new Runnable() {
@Override
public void run() {
String currentDateTime =
DateFormat.getDateTimeInstance().format(new Date());
myList.set(pos, pos + " - " + currentDateTime);
swipeList.invalidateViews();
swipeRefreshLayout.setRefreshing(false);
}
});

}
}).start();
}
}


Keep using the layout in last post.

more:
SwipeRefreshLayout, work with RecyclerView

Wednesday, 18 May 2016

Swipe-to-Refresh ListView using SwipeRefreshLayout


With android.support.v4.widget.SwipeRefreshLayout, user can refresh the contents of a view via a vertical swipe gesture. This example show how to implement Swipe-to-Refresh ListView using SwipeRefreshLayout.


activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:orientation="vertical"
tools:context="com.blogspot.android_er.androidswiperefresh.MainActivity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://android-er.blogspot.com/"
android:textStyle="bold" />

<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swiperefreshlayout"
android:layout_height="match_parent"
android:layout_width="match_parent">
<ListView
android:id="@+id/swipelist"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.v4.widget.SwipeRefreshLayout>

</LinearLayout>


MainActivity.java
package com.blogspot.android_er.androidswiperefresh;

import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;

import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class MainActivity extends AppCompatActivity {

SwipeRefreshLayout swipeRefreshLayout;
ListView swipeList;

List<String> myList;
ListAdapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

swipeRefreshLayout = (SwipeRefreshLayout)findViewById(R.id.swiperefreshlayout);
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
refresh();
}
});

swipeList = (ListView)findViewById(R.id.swipelist);

myList = new ArrayList<>();
adapter = new ArrayAdapter<String>(
this, android.R.layout.simple_list_item_1, myList);
swipeList.setAdapter(adapter);
}

private void refresh(){
//insert dummy string of current date/time
String currentDateTime = DateFormat.getDateTimeInstance().format(new Date());
myList.add(currentDateTime);
swipeList.invalidateViews();
swipeRefreshLayout.setRefreshing(false);
}
}


In this example, the ListView items is updated in SwipeRefreshLayout.OnRefreshListener(). But normally refreshing involve longtime task, such as loading from Internet. The next example show how to refresh with longtime task run in background thread.