In my Android app I have a grid showing images as items. When I click a button the items should be change their positions. I am populating this grid through a cursor adapter. Previously it was working fine but was taking some time to change the positions of the images and refresh the grid once again. So for that i implemented a progress dialog, so that user could understand that something is going on.
Here is my code so far.
My Handler
public void handleMessage(Message msg) {
switch (msg.what) {
case PROGRESS_DIALOG_HANDLER_ID:
progressDialog.dismiss(); //ProgressDialog
DBAdapter adapter = new DBAdapter(SplittedImageActivity.this);
adapter.open();
Cursor cursor = adapter.getAllImages();
adapter.close();
startManagingCursor(cursor);
cursorAdapter.changeCursor(cursor); //My cursor adapter
gridView.setAdapter(cursorAdapter);
cursor.close();
My onclick method
progressDialog = ProgressDialog.show(SplittedImageActivity.this, "", "Please wait...");
new Thread(){
public void run() {
Random random = new Random();
DBAdapter adapter = new DBAdapter(getApplicationContext());
adapter.open();
int childs = gridView.getChildCount(), oldPosition, newPotision;
newPotision = childs-1;
for(int i=0 ; i<childs ; i++){
oldPosition = random.nextInt(m_cGridView.getChildCount());
adapter.updatePosition(oldPosition, newPotision); //updates the position of the images in database
newPotision = oldPosition;
}
adapter.close();
handler.sendEmptyMessage(PROGRESS_DIALOG_HANDLER_ID);
};
}.start();
Problems:
The progress dialog is showing eprfectly, the positions are also changed in the database. but the gridview is not refreshing. I mean after all works done the progress dialog disappears and the screen becomes blank.
Please help me where i am doing wrong?
I think the problem is that you are assigning a new adapter on the handler, but not calling to NotifyDataSetChanged() on the main thread, that's maybe why is not updating the grid.
Also, why not use AsyncTasks?
public class LoadAsyncTask extends AsyncTask<Void, Void, Boolean> {
Context context;
public LoadAsyncTask(Context context) {
this.context = context;
}
protected void onPreExecute() {
}
protected Boolean doInBackground(Void... v) {
DBAdapter adapter = new DBAdapter(context);
adapter.open();
int childs = gridView.getChildCount(), oldPosition, newPotision;
newPotision = childs-1;
for(int i=0 ; i<childs ; i++){
oldPosition = random.nextInt(m_cGridView.getChildCount());
adapter.updatePosition(oldPosition, newPotision); //updates the position of the images in database
newPotision = oldPosition;
}
adapter.close();
DBAdapter adapter = new DBAdapter(context);
adapter.open();
Cursor cursor = adapter.getAllImages();
adapter.close();
startManagingCursor(cursor);
cursorAdapter.changeCursor(cursor); //My cursor adapter
gridView.setAdapter(cursorAdapter);
cursor.close();
return true;
}
protected void onPostExecute(Boolean success) {
if (success) {
//This will be executed on the main activity thread
cursorAdapter.notifyDataSetChanged();
} else {
showError();
}
}
}
Related
I have written a program to add mobile no into my sqlite database on a button click which is working properly , I am also using a listview to show the data added for which I am using a CustomCursorLoader class to query my results .
The problem which I am facing is , suppose I have nothing in my database so the cursor count is 0 but when I insert a data for the first time , the cursor count should become 1 but it shows 0 , and then again when I insert another data at that moment i am getting cursor count as 1 but the data which was previously inserted is being shown in the listview
Posting my code
public class Home_Page extends Activity implements
LoaderManager.LoaderCallbacks<Cursor> {
DriverStatusAdapter driverStatusAdapter;
ListView listDriverId;
private static final int URL_LOADER = 0;
CustomCursorLoader loader = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
try{
dbListHelper = new DriverSqliteHelper(getBaseContext());
dbListHelper.open(getBaseContext());
}catch (Exception e){
e.printStackTrace();
}
String[] columns = new String[]
{DriverSqliteHelper.DbListHelper.DRIVER_USER_ID};
int[] to = new int[]{R.id.DriverId};
driverStatusAdapter = new DriverStatusAdapter(getBaseContext(),
R.layout.view_userid_item,null,columns,to,0);
listDriverId = (ListView) findViewById(R.id.driverIDList);
listDriverId.setAdapter(driverStatusAdapter);
registerForContextMenu(listDriverId);
Log.i("LoaderManager", "Started on activity start");
getLoaderManager().initLoader(0, null, Home_Page.this);
txtAdd.setOnClickListener(new View.OnClickListener() {
String userId = edtUserId.getText().toString();
if (userId.equals(""))
{
Snackbar snackbar = Snackbar.make(coordinatorLayout, "Please
enter user id", Snackbar.LENGTH_LONG);
View sbView = snackbar.getView();
TextView textView = (TextView)
sbView.findViewById(android.support.design.R.id.
snackbar_text);
snackbar.show();
}
else{
sendUserStatus(); ///// method to send mobile no to server
//// if status received from server is ok then i am inserting
////the data into the database
Log.i("LoaderManager", "Restarted on button click");
getLoaderManager().restartLoader(0, null, Home_Page.this);
}
#Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
switch (i){
case URL_LOADER:
Log.i("Case URL Loader", "Custom Cursor Loader called");
loader = new CustomCursorLoader(getBaseContext());
return loader;
default:
Log.i("Case default", "Default Case called");
return null;
}
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
Log.i("LoaderManager", "Finished load entry... - Cursor: " +
cursor.getCount());
this.loader = (CustomCursorLoader)loader;
driverStatusAdapter.changeCursor(cursor);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
Log.i("LoaderManager", "Resetting loader...");
driverStatusAdapter.changeCursor(null);
}
}
CustomCursorLoader.java
public class CustomCursorLoader extends CursorLoader{
Context context;
DriverSqliteHelper driverSqliteHelper;
Cursor cursor;
public CustomCursorLoader(Context context) {
super(context);
try {
driverSqliteHelper = new DriverSqliteHelper(context);
driverSqliteHelper.open(context);
}catch (Exception e){
e.printStackTrace();
}
}
public Cursor loadInBackground(){
cursor = driverSqliteHelper.getDriverStatus();
return cursor;
}
}
My Logcat
I/LoaderManager﹕ Started on activity start
I/Case URL Loader﹕ Custom Cursor Loader called
I/LoaderManager﹕ Finished load entry... - Cursor: 2
********on my first button click ********
I/LoaderManager﹕ Restarted on button click
I/Case URL Loader﹕ Custom Cursor Loader called
I/LoaderManager﹕ Finished load entry... - Cursor: 2
********* on my second button click ********
I/LoaderManager﹕ Restarted on button click
I/Case URL Loader﹕ Custom Cursor Loader called
I/LoaderManager﹕ Finished load entry... - Cursor: 3
I want my cursor count to change on first button click itself , can anyone suggest me what changes do i need to make ?
Ok i have found the solution myself , i put the getLoaderManager().restartLoader(0, null, Home_Page.this); inside sendUserStatus() method where i am also inserting the data.
Now the cursor count is incrementing and the listview is also getting updated automcatically
I have a view with a button and a list view backed by a cursor adapter containing bindView() and newView() for customized views. Each row of a list contains a Text and a checkbox. The data for each view comes from the database. I'm passing my Database adapter in the cursor adapter constructor. This I use to update the database when a checkbox is check or unchecked (works well). Of course I run "re-query" on cursor and view.refreshDrawableState()). Is this a good idea? What would be a better solution?
Second problem more serious, when a Button is clicked it starts a new activity. After hitting the back button from the new activity I get back my list View. But when I try to click on the checkbox this time I get Database close exception. Why? How do I fix this error?
Following is the list view and code snippet.
Button --------> Starts a new activity
CheckBox | TextView
CheckBox | TextView
MyActivity.java
onCreate() {
...
Button add_item_btn = (Button) findViewById(R.id.add_item_btn_id);
add_item_btn.setOnclickListener(new OnClickListener() {
//Start a new activity
});
}
protected void onPause() {
adapter.close();
mCursor.close();
}
protected void onResume() {
mListView = getListView();
adapter = new DBAdapter(getApplication());
adapter.open();
mCursor = adapter.getAllItems();
mCustomAdapter = new MyCursorAdapter(MyActivity.this, mCursor, adapter);
mListView.setAdapter(mCustomAdapter);
}
MyCursorAdapter.java
public class MyCursorAdapter extends CursorAdapter {
Cursor mCursor;
DBAdapter adapter;
public MyCursorAdapter(Context context, Cursor c, DBAdapter _adapter) {
...
mCursor = c;
adapter = _adapter;
}
public void bindView(final View view, Context context, final Cursor cursor) {
final CheckBox itemStatusCB = (CheckBox)
view.findViewById(R.id.item_status_id);
idx = cursor.getColumnIndex(myItem.ITEM_STATUS);
final long itemStatus = cursor.getLong(idx);
if (itemStatus == 1) {
itemStatusCB.setChecked(true);
} else {
itemStatusCB.setChecked(false);
}
itemStatusCB.setOnClickListener(new OnClickListener() {
#Override public void onClick(View v) {
int newStatus = 0;
if (((CheckBox) v).isChecked()) {
newStatus = 1;
}
adapter.updateItemStatus(itemId, newStatus);
mCursor.requery();
view.refreshDrawableState();
});
}
}
}
I was able to solve this. The new activity which was called had a DB connection open on onStart() and DB close on onDestroy(). After returning from that activity I was getting Database Illegal state Exception error as described with stack trace. I think it was returning cached version of DB connection. Once I removed DB.close() from the guest activity, it stopped issuing database not open error. Normally you would think that every activity can open a DB connection in it's onResume() or onStart() and close it in it's onPause() or onStop() or onDestroy() and it won't affect the connection across activities. Does this Make sense?
I am very new to Android and am trying to create an app for travel help. It has a couple of components including -> enabling the user to customize a checklist. Apart from the default list, items can be added and deleted.
For adding,
I'm using a dynamic layout through the class file with no XML. It works perfectly :)
For deleting an item from the list,
I created an adpater, a listview and am trying to delete the selected item. With the help of a couple of "toasts", I am able to derive that, an item is being deleted from the list, but the view is not getting updated.
I have checked and tried numerous solutions, but none of them seem to be working. I am attching my java file, in which the display and customization of the list takes place.
The code seems a little long, but its fairly easy to understand. Any help would be greatly appreciated! :)
public class Dynamic extends ListActivity{
public String[] A = new String[100];
public String[] B = new String[100];
public int j=0, m=0, b=0, tot1=0, tot2=0;
public int a[]=new int[100];
CheckBox c, cc;
ArrayList<String> list2 = new ArrayList<String>();
ArrayList<String> list4 = new ArrayList<String>();
ArrayAdapter<String> adapter;
ArrayAdapter<String> adapter2;
private SparseBooleanArray sba;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ScrollView sv = new ScrollView(this);
final LinearLayout ll = new LinearLayout(this);
ll.setOrientation(LinearLayout.VERTICAL);
sv.addView(ll);
final ListView list3=new ListView(this);
list3.setId(android.R.id.list);
list3.setChoiceMode(list3.CHOICE_MODE_MULTIPLE);
ll.addView(list3);
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_multiple_choice, list2);
Bundle extras = getIntent().getExtras();
if (extras != null) {
A = extras.getStringArray("var");
int i = extras.getInt("var2");
B = extras.getStringArray("var3");
int k = extras.getInt("var4");
for (j=0;j<i;j++)
{
cc = new CheckBox(this);
cc.setText(A[j]);
ll.addView(cc);
list2.add(A[j]);
adapter.notifyDataSetChanged();
}
for (m=0;m<k;m++)
{
cc = new CheckBox(this);
cc.setText(B[m]);
ll.addView(cc);
list2.add(B[m]);
adapter.notifyDataSetChanged();
}
}
Button b = new Button(this);
b.setText("Delete Item");
ll.addView(b);
b.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
AlertDialog.Builder alertDialog = new AlertDialog.Builder(Dynamic.this);
alertDialog.setTitle("Confirm Delete...");
alertDialog.setMessage("Are you sure you want to delete the selected item from the list?");
alertDialog.setPositiveButton("YES", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog,int which)
{
sba=new SparseBooleanArray();
sba.clear();
sba=list3.getCheckedItemPositions();
ListView lv = getListView();
Toast.makeText(getApplicationContext(), "checked " + sba, Toast.LENGTH_SHORT).show();
int itemCount = getListView().getCount();
Toast.makeText(getApplicationContext(), "calc done " + itemCount, Toast.LENGTH_SHORT).show();
for(int i=itemCount-1; i >= 0; i--){
Toast.makeText(getApplicationContext(), "in the loop " + i, Toast.LENGTH_SHORT).show();
Toast.makeText(getApplicationContext(), " " + sba.get(i), Toast.LENGTH_SHORT).show();
list2.add((list2.get(i)));
Toast.makeText(getApplicationContext(), " " + list2.get(i), Toast.LENGTH_SHORT).show();
adapter.remove(list2.get(i));
list3.invalidate();
adapter.notifyDataSetChanged();
list3.setAdapter(adapter);
Toast.makeText(getApplicationContext(), " " + list2, Toast.LENGTH_SHORT).show();
}
sba.clear();
}
});
alertDialog.setNegativeButton("NO", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int which){
Toast.makeText(getApplicationContext(), "The item has NOT been deleted!", Toast.LENGTH_SHORT).show();
dialog.cancel();
}
});
alertDialog.show();
setListAdapter(adapter);
}
});
this.setContentView(sv);
}
}
in order to update your listView try this in your adampter
notifyDataSetChanged();
But You should run it on the UI thread. Create an handler within the UI thread and then post Runable to it
like this
private class Asyn_SaveData extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
//Do something here that will run in backGround
}
#Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
//and this function is called automatically by doInBackground
// after it finish its work
//in your example refresh your ListView
notifyDataSetChanged();
}
}
and to use the AsyncTask
new Asyn_SaveData().execute(null,null,null);
Just remembre AsyncTask must be subClassed
or
myListView.invalidateViews();
I have a listview that contains values from webservice.Each page contains only 10 listitems and the next 10 in page 2 etc.Each listitem is clickable and it contains a button which is mainly for voting.So when i click the button in list item 1 ,a value is added to webservice.
The button click codes are placed in a custom base adapter class.So that i can easily add the vote.But the problem is,When i submit the vote,i want to refresh my listview also.Suppose if iam in page no 5,refresh that listview page.
How can i refresh this listview instantly after submitting the value to webservice?
sample code for main.java
private class ProgressThreadNextPageLoading extends
AsyncTask<String, Void, String> {
// private String Content;
#Override
protected void onPreExecute() {
progressDialog = new ProgressDialog(KidsCrackMeUp.this);
progressDialog.setMessage("Loading..Please Wait..");
progressDialog.setIcon(R.drawable.icon);
progressDialog.show();
}
#Override
protected String doInBackground(String... urls) {
String response = "";
// call ur webservice here
try {
// pagenum = 1;
posts= web
.getAllposts(pagenum);
response = "Yes";
} catch (Exception e) {
e.printStackTrace();
response = "Failure";
}
return response;
}
#Override
protected void onPostExecute(String result) {
// below line code is to dismiss the progress bar
progressDialog.dismiss();
if (posts != null) {
adapter = new DynamicListAdapter(
main.this, posts
lstPosts.setAdapter(adapter);
adapter.notifyDataSetChanged();
//btnState.setPressed(true);
}
----------------------------------custom adapter class
viewHolder.btnVoting.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
final Dialog d = new Dialog(activity);
d.requestWindowFeature(Window.FEATURE_NO_TITLE);
d.setContentView(R.layout.voteselectornew);
Button btnCategoryCancel = (Button) d
.findViewById(R.id.btnCategoryCancel);
Button twcVote = (Button) d
.findViewById(R.id.btnTwcVote);
twcVote.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
String confirm = web
.addTwcVote(UserSessionKey, Userlist.get(position).contentid);
if (confirm.contains("Successfully")) {
d.dismiss();
}
You have to notify your ListView adapter that the data has changed.
listViewAdapater.notifyDataSetChanged();
you can just reasing your adapter via the constructor with the updated array.
call your listview adapter's method to update the change as:
adapter.notifyDataSetChanged();
I'm trying to call .notifyDataSetChange() on a SimpleCursorAdapter displayed in a ListView from an XML-parsing non-UI thread and can't for the life of me figure out how. I've searched all over and all I've found are articles that talk about refreshing from within the ListView itself, which I'm not doing. I can't figure out a way to pass in the adapter or get it from the parent or whatever I need to do to call a method on it from another thread.
The ListView will update fine the next time I launch the activity, but I want it to refresh as soon as the XML parsing is done so that the user will see the new data immediately.
The answer's probably simple; it's just eluding me. Thanks in advance for any help!
Here's my code:
public class DirectoryListActivity extends DirectoryActivity {
public final Handler mHandler = new Handler();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.directory_list);
// Populate the ListView
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(
directoryPeople.PEOPLE_TABLE
);
String asColumnsToReturn[] = {
//snip
};
mCursor = queryBuilder.query(mDB, asColumnsToReturn, null, null,
null, null, directoryPeople.DEFAULT_SORT_ORDER);
startManagingCursor(mCursor);
// HERE'S THE ADAPTER
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
R.layout.directory_people_item, mCursor,
new String[]{
//snip
new int[]{
//snip
);
ListView av = (ListView)findViewById(R.id.listPeople);
av.setAdapter(adapter);
//Perform sync in background
startXMLParseThread();
}
public void startXMLParseThread() {
new Thread () {
boolean success = false;
public void run() {
try {
// XML-Parsing and Table-Updating code
} catch (Exception e) {
success = false;
}
mHandler.post(new Runnable() {
public void run() {
TextView txtUpdateStatus = (TextView)findViewById(R.id.txtUpdateStatus);
if (success) {
txtUpdateStatus.setText(R.string.synced);
} else {
txtUpdateStatus.setText(R.string.sync_failed);
}
adapter.notifyDataSetChanged(); // ECLIPSE HATES
}
});
}
}.start();
}
}
}
No need to create a new adapter...
.notifyDataSetChanged() should be called only in case the data rows actually changed (inserted or deleted rows), in case you just updated the values on rows a simple call to requery() on your cursor should be enough:
adapter.getCursor().requery();
Edit: by your comment I see that you have in fact a compilation problem...
You must declare the adapter as a class member (before/after mHandler declare it: private SimpleCursorAdapter adapter)
Then when you initialize it, replace
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
R.layout.directory_people_item, mCursor,
new String[]{
//snip
new int[]{
//snip
);
with:
adapter = new SimpleCursorAdapter(this,
R.layout.directory_people_item, mCursor,
new String[]{
//snip
new int[]{
//snip
);
Create a new adapter when the query is finished and set it your listview to give a general idea below is an example of gridview change it accordingly to suit your needs
globalAdapter = new GridImageAdapter(getApplicationContext());
globalAdapter.notifyDataSetChanged();
grid.setAdapter(globalAdapter);
grid.invalidateViews();