Hi folks. This time I’m presenting a huge post.
This post will cover following things:
- ListView with separator
- Pagination like Android Market application
I know that not all of you knows ColdFusion and its component (cfc), but it is just a server part like your web service code.
myWebservice.cfc
<cfcomponent displayname="myWebservice"> <cffunction name="getJobs" access="remote" output="false" returntype="struct"> <cfargument name="currentPage" required="true" type="numeric" default='1' /> <cfargument name="totalRecord" required="true" type="numeric" default="10" /> <cfargument name="searchString" required="false" type="string" default="" /> <cfset var qResult = ""> <cfset var condition = "" /> <cfset var startRow = (arguments.CurrentPage-1) * arguments.TotalRecord +1 /> <cfset var endRow = startRow + arguments.TotalRecord -1 /> <cfset var qGetAllRecord = "" /> <cfset var gridStruct = "" /> <cfif len(arguments.searchString)> <cfset condition = condition & " -- your search conditions goes here" /> </cfif> <cfstoredproc procedure="usp_Pagination" datasource="#application.DSN#" username="#application.DBUID#" password="#application.DBPWD#"> <cfprocresult name="qGetAllRecord" resultset="1"> <cfprocresult name="qtotalRecord" resultset="2"> <cfprocparam dbvarname="@SqlColumns" cfsqltype="CF_SQL_VARCHAR" value="*"> <cfprocparam dbvarname="@SqlFriendlyColumns" cfsqltype="CF_SQL_VARCHAR" value="*"> <cfprocparam dbvarname="@SqlTableClause" cfsqltype="CF_SQL_VARCHAR" value="Jobs"> <cfprocparam dbvarname="@StartRow" cfsqltype="CF_SQL_VARCHAR" value="#startRow#"> <cfprocparam dbvarname="@EndRow" cfsqltype="CF_SQL_VARCHAR" value="#endRow#"> <cfprocparam dbvarname="@SqlWhere" cfsqltype="CF_SQL_VARCHAR" value="#condition#"> <cfprocparam dbvarname="@SqlRowNumOrderBy" cfsqltype="CF_SQL_VARCHAR" value="Jobs.createdDate desc"> <cfprocparam dbvarname="@SqlOuterOrderBy" cfsqltype="CF_SQL_VARCHAR" value="createdDate desc"> </cfstoredproc> <cfset gridStruct=StructNew() /> <cfset gridStruct.query = qGetAllRecord /> <cfset gridStruct.totalRowCount=qtotalRecord.countAll /> <cfreturn qResult> </cffunction> </cfcomponent>
The reason behind showing this code is to let you know the implementation of the server part.
In above function, I am returning fix number of records from my database. It is used to pull out ten ten records from a query that has thousands of records.
Now I’m calling this web service like:
http://localhost/cfc/mywebservice.cfc?returnformat=json&method=getJobs¤tPage=5&totalRecord=10
And it returns a data in JSON format like:
{“QUERY”:{“COLUMNS”:[“JOBID”,”JOBTITLE”,”CREATEDATE”,”JOBDETAIL”],”DATA”:[[“1″,”Title1″,”January, 25 2011 10:55:53″,”Detail is here”],[“2″,”Title2″,”January, 25 2011 10:55:53″,”Detail is here”],[“3″,”Title3″,”January, 20 2011 14:13:10″,”Detail is here”],[“4″,”Title4″,”January, 20 2011 14:13:10″,”Detail is here”],[“5″,”Title5″,”January, 20 2011 14:13:10″,”Detail is here”],[“6″,”Title6″,”January, 20 2011 14:13:10″,”Detail is here”],[“7″,”Title7″,”January, 18 2011 16:23:28″,”Detail is here”],[“8″,”Title8″,”January, 17 2011 11:22:02″,”Detail is here”],[“9″,”Title9″,”January, 11 2011 09:22:32″,”Detail is here”],[“10″,”Title10″,”January, 11 2011 09:22:32″,”Detail is here”]]},”TOTALROWCOUNT”:”120″}
Now we are ready to build Android app.
joblist.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> <EditText android:id="@+id/JobSearch" android:layout_height="wrap_content" android:layout_width="fill_parent" android:singleLine="true" android:hint="Search"/> <ListView android:id="@+id/JobList" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <TextView android:id="@+id/EmptyJobList" android:layout_width="fill_parent" android:layout_height="fill_parent" android:text="No Results" android:visibility="invisible" /> </LinearLayout>
joblistheader.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/JobDate" android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center" style="?android:attr/listSeparatorTextViewStyle" /> </LinearLayout>
joblistitem.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="wrap_content" android:paddingRight="5px"> <TextView android:id="@+id/JobTitle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:text="jobTitle" style="?android:attr/textAppearanceLarge" /> <TextView android:id="@+id/JobDetail" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout>
And MyJobListView.java
package com.isummation.listview; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Date; import java.util.TreeSet; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.HttpContext; import org.json.JSONArray; import org.json.JSONObject; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.BaseAdapter; import android.widget.EditText; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; public class MyListView extends Activity { private static boolean isInitialized; private static int JOBID_IDX; private static int JOBTITLE_IDX; private static int CREATEDDATE_IDX; private static int JOBDETAIL_IDX; private JobListAdapter jobListAdapter; private long TotalRowCount; private int totalRecordPerCall = 20; private Date lastDate; private EditText JobSearch; private ListView list; class JobListRowData { boolean isHeader; Date jobCreationDate; int jobId; String jobTitle; String jobDetail; } class JobListAdapter extends BaseAdapter { private static final int TYPE_ITEM = 0; private static final int TYPE_SEPARATOR = 1; private static final int TYPE_MAX_COUNT = TYPE_SEPARATOR + 1; private LayoutInflater mInflater; private ArrayList mData = new ArrayList(); private TreeSet mSeparatorsSet = new TreeSet(); private int count = 0; private boolean isCleared = true; public JobListAdapter() { mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); initialize(); } public void ParseJSONArray(JSONArray JQueryData) { int len = JQueryData.length(); final TextView EmptyListView = (TextView) findViewById(R.id.EmptyJobList); if (TotalRowCount == 0) { EmptyListView.setVisibility(View.VISIBLE); } else { EmptyListView.setVisibility(View.INVISIBLE); } try { for (int i = 0; i < len; i++) { Date tempDate = new Date(JQueryData.getJSONArray(i) .getString(CREATEDDATE_IDX)); if (lastDate == null) { JobListRowData jobListRowData = new JobListRowData(); jobListRowData.isHeader = true; jobListRowData.jobCreationDate = new Date(JQueryData.getJSONArray(i) .getString(CREATEDDATE_IDX)); addSeparatorItem(jobListRowData); jobListRowData = new JobListRowData(); jobListRowData.jobId = JQueryData.getJSONArray(i) .getInt(JOBID_IDX); jobListRowData.jobTitle = JQueryData.getJSONArray(i) .getString(JOBTITLE_IDX); jobListRowData.jobDetail = JQueryData.getJSONArray(i) .getString(JOBDETAIL_IDX); addItem(jobListRowData); lastDate = new Date(JQueryData.getJSONArray(i) .getString(CREATEDDATE_IDX)); } else if (lastDate.getDate() == tempDate.getDate() && lastDate.getMonth() == tempDate.getMonth() && lastDate.getYear() == tempDate.getYear()) { JobListRowData jobListRowData = new JobListRowData(); jobListRowData.jobId = JQueryData.getJSONArray(i) .getInt(JOBID_IDX); jobListRowData.jobTitle = JQueryData.getJSONArray(i) .getString(JOBTITLE_IDX); jobListRowData.jobDetail = JQueryData.getJSONArray(i) .getString(JOBDETAIL_IDX); addItem(jobListRowData); } else { JobListRowData jobListRowData = new JobListRowData(); jobListRowData.isHeader = true; jobListRowData.jobCreationDate = new Date(JQueryData.getJSONArray(i) .getString(CREATEDDATE_IDX)); addSeparatorItem(jobListRowData); lastDate = new Date(JQueryData.getJSONArray(i) .getString(CREATEDDATE_IDX)); jobListRowData = new JobListRowData(); jobListRowData.jobId = JQueryData.getJSONArray(i) .getInt(JOBID_IDX); jobListRowData.jobTitle = JQueryData.getJSONArray(i) .getString(JOBTITLE_IDX); jobListRowData.jobDetail = JQueryData.getJSONArray(i) .getString(JOBDETAIL_IDX); addItem(jobListRowData); } } } catch (Exception e) { Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show(); Log.e(e.getClass().getName(), e.getMessage(), e); TotalRowCount = 0; } } public void initialize() { isCleared = false; count = 0; lastDate = null; mSeparatorsSet.clear(); mData.clear(); JSONArray JQueryData = getJobs(1); ParseJSONArray(JQueryData); isCleared = true; } public void addMoreData() { if (jobListAdapter.count < TotalRowCount && isCleared) { JSONArray JQueryData = getJobs((jobListAdapter.count + totalRecordPerCall) / totalRecordPerCall + 1); ParseJSONArray(JQueryData); } } public void addItem(final JobListRowData item) { mData.add(item); count += 1; notifyDataSetChanged(); } public void addSeparatorItem(final JobListRowData item) { mData.add(item); mSeparatorsSet.add(mData.size() - 1); notifyDataSetChanged(); } public int getItemViewType(int position) { return mSeparatorsSet.contains(position) ? TYPE_SEPARATOR : TYPE_ITEM; } public int getViewTypeCount() { return TYPE_MAX_COUNT; } public int getCount() { return mData.size(); } public JobListRowData getItem(int pos) { return mData.get(pos); } public long getItemId(int pos) { return pos; } @Override public boolean isEnabled(int position) { return mSeparatorsSet.contains(position) ? false : true; } public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; int type = getItemViewType(position); if (convertView == null) { holder = new ViewHolder(); switch (type) { case TYPE_ITEM: convertView = mInflater.inflate(R.layout.joblistitem, null); holder.text = (TextView) convertView .findViewById(R.id.JobTitle); holder.text2 = (TextView) convertView .findViewById(R.id.JobDetail); break; case TYPE_SEPARATOR: convertView = mInflater.inflate(R.layout.joblistheader, null); holder.text = (TextView) convertView .findViewById(R.id.JobDate); break; } convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } switch (type) { case TYPE_ITEM: holder.text.setText(mData.get(position).jobTitle); holder.text2.setText(mData.get(position).jobDetail); break; case TYPE_SEPARATOR: try { holder.text .setText(android.text.format.DateFormat.format( "MMM dd, yyyy", mData.get(position).jobCreationDate)); } catch (Exception e) { Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show(); Log.e(e.getClass().getName(), e.getMessage(), e); } break; } return convertView; } class ViewHolder { TextView text; TextView text2; } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.joblist); JobSearch = (EditText) findViewById(R.id.JobSearch); JobSearch.setImeOptions(EditorInfo.IME_ACTION_SEARCH); JobSearch.setOnKeyListener(new View.OnKeyListener() { public boolean onKey(View v, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN) { jobListAdapter.initialize(); } return false; } }); jobListAdapter = new JobListAdapter(); list = (ListView) findViewById(R.id.JobList); list.setAdapter(jobListAdapter); list.setOnScrollListener(new AbsListView.OnScrollListener() { public void onScrollStateChanged(AbsListView view, int scrollState) { } public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { boolean loadMore = firstVisibleItem + visibleItemCount >= totalItemCount; if (loadMore) { jobListAdapter.addMoreData(); } } }); list.setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView<!--?--> parent, View view, int position, long id) { JobListRowData item = jobListAdapter.getItem(position); if (!item.isHeader) { // start another activity } } }); } public JSONArray getJobs(int currentPage) { try { String searchString = JobSearch.getText().toString(); HttpClient httpClient = new DefaultHttpClient(); HttpContext localContext = new BasicHttpContext(); HttpGet httpGet = new HttpGet( "http://10.0.2.2/cfc/iphonewebservice.cfc?returnformat=json&method=getJobs¤tPage=" + URLEncoder.encode("" + currentPage, "UTF-8") + "&totalRecord=" + totalRecordPerCall + "&searchString=" + URLEncoder.encode(searchString, "UTF-8")); HttpResponse response = httpClient.execute(httpGet, localContext); BufferedReader reader = new BufferedReader(new InputStreamReader( response.getEntity().getContent(), "UTF-8")); String sResponse = reader.readLine(); JSONObject JResponse = new JSONObject(sResponse); JSONObject JQuery = JResponse.getJSONObject("QUERY"); TotalRowCount = JResponse.getLong("TOTALROWCOUNT"); JSONArray JQueryColumns = JQuery.getJSONArray("COLUMNS"); if (!isInitialized) { int len = JQueryColumns.length(); for (int i = 0; i < len; i++) { if (JQueryColumns.getString(i).equals("JOBID")) { JOBID_IDX = i; } else if (JQueryColumns.getString(i).equals("JOBTITLE")) { JOBTITLE_IDX = i; } else if (JQueryColumns.getString(i).equals("CREATEDATE")) { CREATEDDATE_IDX = i; } else if (JQueryColumns.getString(i).equals( "JOBDETAIL")) { JOBDETAIL_IDX = i; } } isInitialized = true; } return JQuery.getJSONArray("DATA"); } catch (Exception e) { Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show(); Log.e(e.getClass().getName(), e.getMessage(), e); finish(); return new JSONArray(); } } }
Update:
I’ve updated above code, for “Loading…” effect while getting next 20 records.
You can now download the source code.
Download source code
You must be logged in to post a comment.