[android] Async search via loader.

This commit is contained in:
d-kunin 2013-08-06 13:07:17 +03:00
parent 4654442ef2
commit 7c15605c1f
4 changed files with 194 additions and 32 deletions

View file

@ -3,21 +3,44 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<EditText
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_weight="0"
android:hint="Enter Search Term"
/>
<include
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_weight="1"
layout="@android:layout/list_content"/>
</LinearLayout>
<LinearLayout
android:id="@+id/searchBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="horizontal" >
<ImageView
android:id="@+id/searchIcon"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="0"
android:src="@android:drawable/ic_menu_search" />
<EditText
android:id="@+id/searchText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="Enter Search Term"
android:inputType="textCapWords"
android:singleLine="true"
android:textStyle="bold" />
<ImageView
android:id="@+id/clearSearch"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="0"
android:src="@android:drawable/ic_delete"
android:visibility="gone" />
</LinearLayout>
<include
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
layout="@android:layout/list_content" />
</LinearLayout>

View file

@ -1,23 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="100dp"
android:gravity="center_vertical"
android:orientation="horizontal" >
<TextView
style="@android:style/TextAppearance.Medium"
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Hello World!" />
<ImageView
android:id="@+id/thumbnail"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_weight="0"
android:scaleType="centerCrop"
android:src="@drawable/ic_launcher" />
<TextView
android:id="@+id/title"
style="@android:style/TextAppearance.Medium"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Hello World!"
android:textStyle="bold" />
</LinearLayout>

View file

@ -3,16 +3,26 @@ package com.example.travelguide;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.support.v4.text.TextUtilsCompat;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import com.example.travelguide.async.QueryResultLoader;
import com.example.travelguide.cpp.Storage;
import com.example.travelguide.dummy.DummyContent;
import com.example.travelguide.widget.StorageArticleInfoAdapter;
import static com.example.travelguide.util.Utils.*;
/**
* A list fragment representing a list of ArticleInfos. This fragment also
* supports tablet devices by allowing list items to be given an 'activated'
@ -22,9 +32,14 @@ import com.example.travelguide.widget.StorageArticleInfoAdapter;
* Activities containing this fragment MUST implement the {@link Callbacks}
* interface.
*/
public class ArticleInfoListFragment extends ListFragment
public class ArticleInfoListFragment extends ListFragment implements LoaderCallbacks<Storage>, TextWatcher,
OnClickListener
{
private View mRootView;
private TextView mSearchText;
private View mCross;
private View mSearchIcon;
private Storage mStorage;
/**
@ -80,9 +95,6 @@ public class ArticleInfoListFragment extends ListFragment
{
super.onCreate(savedInstanceState);
mStorage = new Storage();
// TODO: remove it
mStorage.query("", false, 0, 0);
setListAdapter(new StorageArticleInfoAdapter(mStorage, getActivity()));
}
@Override
@ -109,6 +121,11 @@ public class ArticleInfoListFragment extends ListFragment
}
mCallbacks = (Callbacks) activity;
// Load initial data
final Bundle args = new Bundle(1);
args.putString(KEY_QUERY, "");
getLoaderManager().initLoader(SEARCH_LOADER, args, this).forceLoad();
}
@Override
@ -118,6 +135,8 @@ public class ArticleInfoListFragment extends ListFragment
// Reset the active callbacks interface to the dummy implementation.
mCallbacks = sDummyCallbacks;
getLoaderManager().destroyLoader(SEARCH_LOADER);
}
@Override
@ -165,11 +184,89 @@ public class ArticleInfoListFragment extends ListFragment
mActivatedPosition = position;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
mRootView = inflater.inflate(R.layout.fragment_articleinfo_list, null, false);
mSearchText = (TextView) mRootView.findViewById(R.id.searchText);
mSearchIcon = mRootView.findViewById(R.id.searchIcon);
mCross = mRootView.findViewById(R.id.clearSearch);
// setup listeners
mSearchText.addTextChangedListener(this);
mCross.setOnClickListener(this);
return mRootView;
}
/**
*
* LOADER
*
*/
private static int SEARCH_LOADER = 0x1;
private String KEY_QUERY = "key_query";
@Override
public Loader<Storage> onCreateLoader(int id, Bundle args)
{
if (id == SEARCH_LOADER)
{
final String query = args.getString(KEY_QUERY);
// TODO: add location check
// TODO: add progress
return new QueryResultLoader(getActivity(), query);
}
return null;
}
@Override
public void onLoadFinished(Loader<Storage> loader, Storage result)
{
setListAdapter(new StorageArticleInfoAdapter(result, getActivity()));
}
@Override
public void onLoaderReset(Loader<Storage> loader)
{}
/**
*
* TEXT WATCHER
*
*/
@Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{
final Bundle args = new Bundle(1);
args.putString(KEY_QUERY, s.toString());
getLoaderManager().restartLoader(SEARCH_LOADER, args, this).forceLoad();
hideIf(TextUtils.isEmpty(s), mCross);
}
@Override
public void afterTextChanged(Editable s)
{}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{}
/**
*
* CLICK
*
*/
@Override
public void onClick(View v)
{
if (v.getId() == mCross.getId())
mSearchText.setText(""); // clean up text field
}
}

View file

@ -0,0 +1,40 @@
package com.example.travelguide.async;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
import com.example.travelguide.cpp.Storage;
public class QueryResultLoader extends AsyncTaskLoader<Storage>
{
private final String mQuery;
private final double mLat;
private final double mLon;
private final boolean mUseLocation;
public QueryResultLoader(Context context, String query)
{
super(context);
mQuery = query;
mUseLocation = false;
mLat = mLon = 0;
}
public QueryResultLoader(Context context, String query, double lat, double lon)
{
super(context);
mQuery = query;
mUseLocation = true;
mLat = lat;
mLon = lon;
}
@Override
public Storage loadInBackground()
{
final Storage storage = new Storage();
storage.query(mQuery, mUseLocation, mLat, mLon);
return storage;
}
}