I am working on application, that exists Sqlite Database. What I have done to implement the search on sqlite database using list, I have implemented the like query and I came to know; The searchview is not searching data from sqlite...
Here is my Sqlite File....
public List<GetSetClientsDetail> SearchClientsData() {
String[] columns = {
fname,
route,
cnic,
lname,
contact
};
String sortOrder = fname + " ASC";
List<GetSetClientsDetail> clientlist = new ArrayList<GetSetClientsDetail>();
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.query(
table_poducts_records,
new String[] { fname, route, cnic, lname, contact},
fname + " LIKE '%" + fname + "%'",
null, null, null, null, null);
if (cursor.moveToFirst()) {
do {
GetSetClientsDetail clientsDetail = new GetSetClientsDetail();
clientsDetail.setFNAME(cursor.getString(cursor.getColumnIndex(fname)));
clientsDetail.setROUTE(cursor.getString(cursor.getColumnIndex(route)));
clientsDetail.setCNIC(cursor.getString(cursor.getColumnIndex(cnic)));
clientsDetail.setLNAME(cursor.getString(cursor.getColumnIndex(lname)));
clientsDetail.setCONTACT(cursor.getString(cursor.getColumnIndex(contact)));
clientlist.add(clientsDetail);
} while (cursor.moveToNext());
}
cursor.close();
db.close();
return clientlist;
}
Here is my viewpage class of search where I have implemented search..
public class Clients extends Fragment {
RecyclerView recyclerViewClients;
Button btnAll;
SearchView searchViewclient;
ClientRecyclerAdapter clientRecyclerAdapter;
List<GetSetClientsDetail> listclients;
DatabaseHelper databaseHelper;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.clients, container, false);
btnAll=view.findViewById(R.id.searchallclients);
recyclerViewClients=view.findViewById(R.id.recyclerviewallclients);
searchViewclient=view.findViewById(R.id.searchclient);
listclients = new ArrayList<>();
clientRecyclerAdapter = new ClientRecyclerAdapter(listclients,recyclerViewClients);
recyclerViewClients.setItemAnimator(new DefaultItemAnimator());
recyclerViewClients.setItemAnimator(new DefaultItemAnimator()); //multi copy paste!
recyclerViewClients.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerViewClients.setHasFixedSize(true);
recyclerViewClients.setAdapter(clientRecyclerAdapter);
databaseHelper = new DatabaseHelper(getActivity());
searchViewclient.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
SearchSQliteClientData();
}
});
btnAll.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getClientFromSqlite();
}
});
return view;
}
#SuppressLint("StaticFieldLeak")
private void SearchSQliteClientData() {
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
listclients.clear();
listclients.addAll(databaseHelper.SearchClientsData());
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
clientRecyclerAdapter.notifyDataSetChanged();
}
}.execute();
}
#SuppressLint("StaticFieldLeak")
private void getClientFromSqlite() {
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
listclients.clear();
listclients.addAll(databaseHelper.getAllClientsData());
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
clientRecyclerAdapter.notifyDataSetChanged();
}
}.execute();
}
}
What I want to do is As I press A on searchview; It brings all data related to a or If I press a letter It bring that data in the list I have Implemented..
As an example
change public List<GetSetClientsDetail> SearchClientsData()
to
public List<GetSetClientsDetail> SearchClientsData(String mysearchstring)
this allows the search argument to be passed to the method from the caller
Then change :-
Cursor cursor = db.query(
table_poducts_records,
new String[] { fname, route, cnic, lname, contact},
fname + " LIKE '%" + fname + "%'",
null, null, null, null, null);
to
String[] whereargs = new String[]{"%" + mysearchstring + "%"}; //<<<<<<<<<< ADDED
Cursor cursor = db.query(
table_poducts_records,
new String[] { fname, route, cnic, lname, contact},
fname + " LIKE ?", //<<<<<<<<<< CHANGED
whereargs, //<<<<<<<<<< ADDED
null, null, null); //<<<<<<<<<< 3 nulls instead of 4 (as whereargs has replaced first null)
this uses the search argument passed to the method in the LIKE clause protecting against SQL Injection
As a test try :-
listclients.addAll(databaseHelper.SearchClientsData("A")); //<<<<<<<<<< look for all with A anywhere in the fname column
Working example
The following is a working example based upon the code in the question but simplified for convenience.
The core change is two fold as, the above code is in the database helper DatabaseHelper.java as per :-
That is the SearchClientData method is :-
public List<GetSetClientsDetail> SearchClientsData(String mysearchstring) {
String[] columns = {
fname, route, cnic, lname, contact
};
String sortOrder = fname + " ASC";
List<GetSetClientsDetail> clientlist = new ArrayList<GetSetClientsDetail>();
SQLiteDatabase db = this.getReadableDatabase();
String[] whereargs = new String[]{"%" + mysearchstring + "%"}; //<<<<<<<<<< ADDED
Cursor cursor = db.query(
table_poducts_records,
new String[]{fname, route, cnic, lname, contact},
fname + " LIKE ?",
whereargs,
null, null, sortOrder, null
);
if (cursor.moveToFirst()) {
do {
GetSetClientsDetail clientsDetail = new GetSetClientsDetail();
clientsDetail.setFNAME(cursor.getString(cursor.getColumnIndex(fname)));
clientsDetail.setROUTE(cursor.getString(cursor.getColumnIndex(route)));
clientsDetail.setCNIC(cursor.getString(cursor.getColumnIndex(cnic)));
clientsDetail.setLNAME(cursor.getString(cursor.getColumnIndex(lname)));
clientsDetail.setCONTACT(cursor.getString(cursor.getColumnIndex(contact)));
clientlist.add(clientsDetail);
} while (cursor.moveToNext());
}
cursor.close();
return clientlist;
}
public List<GetSetClientsDetail> getAllClientsData() {
return SearchClientsData("");
}
Note the freebie getAllClientsData which just uses the SearchClientData method passing "", which will select all rows.
The other core change is that instead of relying upon the SearchView's OnClickListener which may well get called due to the Views other Listener's stealing the focus-ability.
The SearchView's setOnQueryTextListener has been utilised. This allows the text to be passed to the SearchClientsData.
For convenience this example utilises an ArrayAdapter and the stock Simple_List_Item1 layout and does the work on the main thread and of activity.
The Activity code used was :-
public class MainActivity extends AppCompatActivity {
ListView listviewClients;
Button btnAll;
ArrayAdapter mSCA;
SearchView searchViewclient;
List<GetSetClientsDetail> listclients;
DatabaseHelper databaseHelper;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnAll=this.findViewById(R.id.searchallclients);
listviewClients=this.findViewById(R.id.clients);
searchViewclient=this.findViewById(R.id.searchclient);
databaseHelper = new DatabaseHelper(this);
addSomeData();
manageListView("");
searchViewclient.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
manageListView("zzz");
}
});
searchViewclient.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
manageListView(newText);
return false;
}
});
btnAll.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
manageListView("");
}
});
}
private void manageListView(String searchArgument) {
listclients = databaseHelper.SearchClientsData(searchArgument);
if (mSCA == null) {
mSCA = new ArrayAdapter(this,android.R.layout.simple_list_item_1,listclients);
listviewClients.setAdapter(mSCA);
} else {
mSCA.clear();
mSCA.addAll(listclients);
mSCA.notifyDataSetChanged();
}
}
private void addSomeData() {
databaseHelper.add("Albert","001","123456789","Smith","someone");
databaseHelper.add("Freda","001","123456789","Jones","someone");
databaseHelper.add("Mike","002","0987654321","Allenby","him");
}
/*
private void SearchSQliteClientData() {
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
listclients.clear();
listclients.addAll(databaseHelper.SearchClientsData());
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
clientRecyclerAdapter.notifyDataSetChanged();
}
}.execute();
}
private void getClientFromSqlite() {
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
listclients.clear();
listclients.addAll(databaseHelper.getAllClientsData());
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
clientRecyclerAdapter.notifyDataSetChanged();
}
}.execute();
}
*/
}
The addSomeData method does as it says adds some testing data. The commented out code is original but unused code.
Result
When first run (not really the 1st, rather after a number of runs BUT freshly started, hence plenty of data) :-
Typing a or A and all the Mike's disappear
and so on, the list is instantly adjusted according to the text entered in the search field. Clicking the ALL button re-displays all.
Related
I access a db in an activity that is used in a different activity. However when i use getContentResolver.update() on the db it won't update in my CursorLoader although it accesses the same db (it's the same queryUri). It shows me the updated value when i dump a query to the db, however the CursorLoader won't.
Here is my onCreateLoader method:
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Uri queryUri;
if(id == ID_LOADER_PORTFOLIO) {
String[] projection = null;
String selection = null;
String[] selectionArguments = null;
queryUri = MainFeedContract.CONTENT_URI;
return new CursorLoader(this,
queryUri,
MainActivity.COLUMN_NAMES,
selection,
selectionArguments,
null);
}
return null;
}
and this is my call to update the db in the same activity:
portfolioBuilder
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
})
.setPositiveButton("Apply",new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
EditText et_newValue = (EditText) view.findViewById(R.id.et_portfolio);
Double value = Double.valueOf(et_newValue.getText().toString());
String selectedSpinnerCurrency = spinner.getSelectedItem().toString();
ContentValues cv = new ContentValues();
cv.put("units",value);
getContentResolver().update(MainFeedContract.CONTENT_URI, cv, "name=?", new String[]{selectedSpinnerCurrency});
Log.d("QUERY", DatabaseUtils.dumpCursorToString(getContentResolver().query(MainFeedContract.CONTENT_URI, null, null, null, null)));
}
});
Am i making a mistake when trying to update the db? :/
You may need to register a ContentObserver
Call this method in onResume()
public void registerDataObserver() {
try {
getContext().getContentResolver().registerContentObserver(MainFeedContract.CONTENT_URI, true, new DataObserver(new Handler()));
} catch (IllegalStateException ise) {
}
}
DataObserver class
class DataObserver extends ContentObserver {
public DataObserver(Handler handler) {
super(handler);
}
#Override
public void onChange(boolean selfChange, Uri uri) {
getLoaderManager().restartLoader(0, null, this);
}
}
I have this class to connect with twitter
public class TwitterConnect {
TwitterApiClient twitterApiClient;
StatusesService statusesService;
Long [] ids;
public TwitterConnect(){
twitterApiClient = TwitterCore.getInstance().getApiClient();
statusesService = twitterApiClient.getStatusesService();
callHomeTimeLine();
}
private void callHomeTimeLine(){
statusesService.homeTimeline(200, null, null, null, null, null, null, new Callback<List<Tweet>>() {
#Override
public void success(Result<List<Tweet>> result) {
Long [] ids = new Long[result.data.size()];
int i=0;
for (Tweet tweet:result.data) {
//tweet.id;
Log.w("Twwet",tweet.id+" ");
ids[i] = result.data.get(i).id;
i++;
}
setIds(ids);
}
#Override
public void failure(TwitterException exception) {
Log.e("FAILURE", "Fallo en callHomeTimeLine");
}
});
}
private void setIds(Long [] ids){
this.ids = ids;
}
public Long[] getIds() {
return ids;
}
}
And I use this clas on an activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_following);
getSupportActionBar().setDisplayShowTitleEnabled(false);
TwitterConnect twc = new TwitterConnect();
this.ids = twc.getIds()//ids its always null
;
But the this.ids is always null,on succces method its asynchronous ,how I can wait for it? In edbug mode ,the data is correctly retrieved but the activity uses the ids array before the asynchronous method finished.
The common way to "wait" for an asynchronous task to finish is (in your case), like this:
In your TwitterConnect Class, add an interface:
interface Idsinterface {
void setIds(Long[] ids);
}
and pass it as parameter to TwitterConnect Constructor, that it looks like this:
public class TwitterConnect {
TwitterApiClient twitterApiClient;
StatusesService statusesService;
Long [] ids;
public TwitterConnect(IdsInterface idsInterface){
twitterApiClient = TwitterCore.getInstance().getApiClient();
statusesService = twitterApiClient.getStatusesService();
callHomeTimeLine(idsInterface);
}
private void callHomeTimeLine(IdsInterface idsInterface){
statusesService.homeTimeline(200, null, null, null, null, null, null, new Callback<List<Tweet>>() {
#Override
public void success(Result<List<Tweet>> result) {
Long [] ids = new Long[result.data.size()];
int i=0;
for (Tweet tweet:result.data) {
//tweet.id;
Log.w("Twwet",tweet.id+" ");
ids[i] = result.data.get(i).id;
i++;
}
idsInterface.setIds(ids);
}
#Override
public void failure(TwitterException exception) {
Log.e("FAILURE", "Fallo en callHomeTimeLine");
}
});
}
private void setIds(Long [] ids){
this.ids = ids;
}
public Long[] getIds() {
return ids;
}
interface IdsInterface {
void setIds(Long[] ids);
}
}
Now you can use this Class like this:
new TwitterConnect(TwitterConnect.IdsInterface() {
#Override
void setIds(Long[] ids) {
YourActivity.this.ids = ids;
//rest of your code here
});
I am fetching records into adapter using addTextChangedListener. If I query through database asynchronously, then there is no lag but I am not getting suggestions in my AutocompleteTextView as soon as I enter the word and if I query on UI thread then there is a serious lag. I already indexed my table.
How can I improve my sqlite database performance.
Query
String where =x+" LIKE ?";
String[] whereArgs = new String[] {column_name+"%"};
cursor = db.query(TABLE_NAME,TABLE_COLUMNS,where,whereArgs, null, null, null, "10");
Asynctask
private class FindText extends AsyncTask<String, Void, Void>
{
private ArrayList<TextModel> textModels;
#Override
protected void onPreExecute() {
super.onPreExecute();
startSppiner("", "", false);
}
#Override
protected Void doInBackground(String... params) {
textModel= new StoreFactory(mActivity, 0).getSchedulesByWordSpecific(params[0].trim());
return null;
}
#Override
protected void onPostExecute(Void result) {
stopSppiner();
for(TextModel textModel: textModels)
{
mAdapter.add(textModel.getWord());
textMap.put(textModel.getText(), textModel);
}
mAdapter.notifyDataSetChanged();
}
}
I am loading images by using AsyncTask and for updating ListView I am using notifyDataSetChanged() method.
But notifyDataSetChanged() doesn't change anything in the ListView onProgressUpdate().
I don't want to use cursor.requery because it is deprecated method.
Why notifyDataSetChanged() is not working for me?
public class News extends BasicActivity implements OnItemClickListener{
private SQLiteDatabase db = null;
private ListView lvNews;
private TaskImgSm taskImgSm = null;
private NewsListAdapter adapter;
private Cursor cur;
boolean pause = false;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news);
db = SQLiteDatabase.openDatabase(((MyApp) getApplication()).getDbPath(), null, SQLiteDatabase.OPEN_READWRITE);
lvNews = (ListView) findViewById(R.id.list_news);
lvNews.setOnItemClickListener(this);
listFilling();
if (taskImgSm != null && taskImgSm.getStatus() != AsyncTask.Status.FINISHED) taskImgSm.cancel(true);
taskImgSm = new TaskImgSm();
taskImgSm.execute();
}
private void listFilling() {
cur = db.query("news", new String[] { "_id", "id_news", "title", "date", "img_url", "img_sm" }, null, null, null, null, null);
startManagingCursor(cur);
adapter = new NewsListAdapter(this, cur, db);
lvNews.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
class TaskImgSm extends AsyncTask<Void, String, Void> {
Cursor curs;
#Override
protected void onPreExecute() {
super.onPreExecute();
curs = db.query("news", new String[] { "_id", "id_news", "img_url", "img_sm" }, null, null, null, null, null);
startManagingCursor(curs);
}
#Override
protected Void doInBackground(Void... unused) {
curs.moveToFirst();
while (curs.isAfterLast() == false) {
if (curs.getBlob(curs.getColumnIndex("img_sm")) == null) {
String imgUrl = curs.getString(curs.getColumnIndex("img_url"));
String idNews = curs.getString(curs.getColumnIndex("id_news"));
updateImg(imgUrl, idNews, "img_sm");
publishProgress();
}
curs.moveToNext();
}
return (null);
}
private void updateImg(String img_URL, String whereId, String imgColumn) {
try {
DefaultHttpClient mHttpClient = new DefaultHttpClient();
HttpGet mHttpGet = new HttpGet();
mHttpGet.setURI(new URI(img_URL));
HttpResponse mHttpResponse = mHttpClient.execute(mHttpGet);
if (mHttpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
HttpEntity entity = mHttpResponse.getEntity();
if (entity != null) {
// insert to database
ContentValues values = new ContentValues();
values.put(imgColumn, EntityUtils.toByteArray(entity));
db.update("news", values, "id_news=" + whereId, null);
}
}
} catch (URISyntaxException e) {e.printStackTrace();
} catch (ClientProtocolException e) {e.printStackTrace();
} catch (IOException e) {e.printStackTrace();}
}
#Override
protected void onProgressUpdate(String... item) {
if (pause == false) {
adapter.notifyDataSetChanged();
}
}
#Override
protected void onPostExecute(Void unused) {}
}
#Override
protected void onPause() {
pause = true;
super.onPause();
}
#Override
protected void onResume() {
pause = false;
adapter.notifyDataSetChanged();
super.onResume();
}
#Override
protected void onDestroy() {
if (taskImgSm != null && taskImgSm.getStatus() != AsyncTask.Status.FINISHED) taskImgSm.cancel(true);
super.onDestroy();
}
}
The reason it's not working is because notifyDataSetChanged() only tells the ListView that the data in the adapter has changed. Since that data hasn't changed (because you haven't queried the database again), then the ListView won't show any updates. You need to execute the query again and update the data in the adapter, then call notifyDatasetChanged().
Why don't use a ContentProvider.
With a ContentProvider you can update your table with the notifyChange(uri) method
Tutorial for that here http://thinkandroid.wordpress.com/2010/01/13/writing-your-own-contentprovider/
I think you may want to update the ImageView in a ListView in the onPostExecute() method. This is how I accomplished something similar to what you are doing : Multithreading For Performance
I face two main problems when using a sqlite command inside an AsncTask in android.
When I execute a select command on the first try I get no results but on the second try (loading a activity that has this Asynctask) I do get results.
Sometimes I get an error that the database is not closed despite that it is already closed/
What is the problem with this?
UPDATE:
This is the code that retrive data from database (db.getAllMessage)
private ArrayList<FriendMessagesResulted> getMessagesFromCach(Context c){
FriendMessagesResulted messagesResulted1 = new FriendMessagesResulted();
DBAdapter db = new DBAdapter(c);
Cursor c1;
db.open();
c1 = db.getAllMessage(Settings.getCurrentUserId(c),Integer.parseInt(fId));
Log.d("***Database count",c1.getCount()+" from: "+Settings.getCurrentUserId(c)+" to:"+Integer.parseInt(fId));
c1.moveToFirst();
if(c1.getCount()>0)
status=true;
if (messagesResultedList == null) {
messagesResultedList = new ArrayList<FriendMessagesResulted>();
}
else
messagesResultedList.clear();
while (c1.isAfterLast() == false) {
if(Integer.parseInt(c1.getString(0))>maxId)
maxId=Integer.parseInt(c1.getString(0));
messagesResulted1.set_mId(Integer.parseInt(c1.getString(0)));
messagesResulted1.set_msgTxt("MD:"+c1.getString(3));
messagesResulted1.set_MessageTime(c1.getString(4));
messagesResulted1.set_dir(c1.getString(5));
messagesResultedList.add(messagesResulted1);
c1.moveToNext();
}
db.close();
c1.close();
return messagesResultedList;
}
and this the code for AsyncTask, where I call get getMessagesFromCach method
private void getMessages(final Context c)
{
handler = new Handler();
r=new Runnable() {
public void run() {
class RecentMessageLoader extends AsyncTask<Void, Void, ArrayList<FriendMessagesResulted>>{
ArrayList<FriendMessagesResulted> messagesResultedList=null;
#Override
protected ArrayList<FriendMessagesResulted> doInBackground(Void... params) {
if(!finishLoadingPastMessages)
{
messagesResultedList=getMessagesFromCach(c);
if(!status){
Log.d("Where are you","I'm in JSON");
messagesResultedList=getMessagesFromJSON(c);
}
}
else{
Log.d("Where are you","I'm in Recent messages");
messagesResultedList=getRecentMessages(c,Settings.getCurrentUserId(c),Integer.parseInt(fId));
}
return messagesResultedList;
}
protected void onPostExecute( ArrayList<FriendMessagesResulted> FMRList ) {
// to disappear loading message
d.dismiss();
finishLoadingPastMessages=true;
if(FMRList!=null){
for(int i=FMRList.size()-1;i>=0;i--)
addMessage(FMRList.get(i),c);
}
handler.postDelayed(r, 2000);
}
}
new RecentMessageLoader().execute();
}
};
handler.post(r);
}
UPDATE 2 : Cach class ..
public class Cach {
static DBAdapter db;
public Cach(Context c)
{
}
public static void AddMessages(Context c,
int id,
int fromId,
int toId,
String message,
String dir,
String MessageTime)
{
db = new DBAdapter(c);
db.open();
long id2;
id2 = db.insertMessage(id, fromId, toId, message, dir,MessageTime);
db.close();
}
}
It seems the problem is with the type of variables you are using.. there must be Static variables of instance variables which are getting set from many sources... try not to use static variables and use local variables I mean in the methods implicitly.