I'm trying to implement a loader which should load some data from a restful api using retrofit. However the content isn't loaded... unless I place a call to workspaceAdapter.notifyDataSetChanged() in my onOptionsItemSelected method. I say place a call because I don't actually have to make the call. Which I find very odd.
The second odd thing about this is that if I change some data on the server and tries to update by click the refresh menu item which should call the same workspaceAdapter.notifyDataSetChanged() nothing happens.
Below is my Activity.
public class WorkspacesActivity extends Activity implements LoaderCallbacks<List<ListviewEntry>> {
private static final String TAG = WorkspacesActivity.class.getSimpleName();
private static final int LOADER_ID = 1;
private RestClient client;
private ProgressBar progressBar;
private WorkspaceAdapter workspaceAdapter;
private ListView listView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_workspaces);
workspaceAdapter = new WorkspaceAdapter(this);
listView = (ListView)findViewById(R.id.listView);
listView.setAdapter(workspaceAdapter);
progressBar = (ProgressBar)findViewById(R.id.workspacesProgressBar);
progressBar.setVisibility(View.VISIBLE);
Bundle extras = getIntent().getExtras();
if(extras == null) {
return;
}
String accessToken = extras.getString(MainActivity.INTENT_ACCESS_TOKEN);
if(accessToken != null) {
client = new RestClient(accessToken);
getLoaderManager().initLoader(LOADER_ID, null, this);
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
switch (item.getItemId())
{
case R.id.action_refresh:
workspaceAdapter.notifyDataSetChanged();
// getLoaderManager().getLoader(LOADER_ID).forceLoad();
return true;
case R.id.action_create_organization:
return true;
case R.id.action_settings:
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public Loader<List<ListviewEntry>> onCreateLoader(int i, Bundle bundle) {
Log.d(TAG, "onCreateLoader");
progressBar.setVisibility(View.VISIBLE);
return new WorkspaceLoader(this, client);
}
#Override
public void onLoadFinished(Loader<List<ListviewEntry>> listLoader, List<ListviewEntry> listviewEntries) {
Log.d(TAG, "onLoadFinished");
progressBar.setVisibility(View.GONE);
workspaceAdapter.setData(listviewEntries);
}
#Override
public void onLoaderReset(Loader<List<ListviewEntry>> listLoader) {
Log.d(TAG, "onLoaderReset");
progressBar.setVisibility(View.VISIBLE);
workspaceAdapter.setData(null);
}
}
My loader.
public class WorkspaceLoader extends AsyncTaskLoader<List<ListviewEntry>> {
private static final String TAG = WorkspaceLoader.class.getSimpleName();
private RestClient client;
public WorkspaceLoader(Context context, RestClient client) {
super(context);
this.client = client;
}
#Override
public void onStartLoading() {
Log.d(TAG, "onStartLoading");
forceLoad();
super.onStartLoading();
}
/**
* Since Organization has (1:m) Workspaces. We need to flatten this structure. A
* List<ListviewEntry> is used as internal data source. So this method request all the
* oganizations associated with a user, extracts organization or workspace id, organization or
* workspace name and stores those with a type indicating weather it's one or the other. This
* list structure can then be passed on to the adapter.
*
* #return listviewEntries
*/
#Override
public List<ListviewEntry> loadInBackground() {
Log.d(TAG, "loadInBackground");
List<Organization> organizations = client.requestOrganizations();
List<ListviewEntry> listviewEntries = new ArrayList<ListviewEntry>();
// Flatten Organizations and Workspaces
for (Organization organization : organizations) {
listviewEntries.add(new ListviewEntry(organization.getOrg_id(), organization.getName(),
ListviewEntry.Type.ORGANIZATION));
for (Workspace workspace : organization.getSpaces()) {
listviewEntries.add(new ListviewEntry(workspace.getSpace_id(), workspace.getName(),
ListviewEntry.Type.WORKSPACE));
}
}
return listviewEntries;
}
#Override
public void deliverResult(List<ListviewEntry> data) {
Log.d(TAG, "deliverResult");
if(isReset())
{
if(data != null) {
releaseResources(data);
return;
}
}
super.deliverResult(data);
}
private void releaseResources(List<ListviewEntry> data) {
Log.d(TAG, "releaseResources");
// For a simple List, there is nothing to do. For something like a Cursor,
// we would close it in this method. All resources associated with the
// Loader should be released here.
}
}
My adapter.
public class WorkspaceAdapter extends BaseAdapter {
private static final String TAG = WorkspaceAdapter.class.getSimpleName();
private List<ListviewEntry> data;
private LayoutInflater layoutInflater;
public WorkspaceAdapter(Context context) {
data = new ArrayList<ListviewEntry>();
layoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public void setData(List<ListviewEntry> data) {
this.data = data;
}
#Override
public int getCount() {
return data.size();
}
#Override
public ListviewEntry getItem(int position) {
return data.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
switch (getItem(position).getType()) {
case WORKSPACE:
convertView = layoutInflater.inflate(R.layout.listview_workspace, null);
holder.textView = (TextView) convertView.findViewById(R.id.listViewRow);
break;
case ORGANIZATION:
convertView = layoutInflater.inflate(R.layout.listview_organization, null);
holder.textView = (TextView) convertView.findViewById(R.id.listViewHeader);
break;
}
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.textView.setText(data.get(position).getName());
return convertView;
}
public static class ViewHolder {
public TextView textView;
}
}
Anyone got a clue about where I'm going wrong?
I've updated my setData method on my adapter si calls notifyDataSetChanged. Eg.
public void setData(List<ListviewEntry> data)
{
this.data = data;
notifyDataSetChanged();
}
That seems to do the trick. But is that how it should be done?
Related
I'm storing a list of the most frequented transit lines in a Content Provider and a RecyclerView Adapter to be available in online and offline viewing. They are downloaded into an activity via AsyncTaskLoader. They are accessible from the menu option in the activity below. I debugged and it shows that items are being added to the Content Provider in another activity. Then, when I click on "most frequented" in the menu option, the screen is blank but the items are clickable(both on and offline). I've tried to debug but it's not showing any errors.
I found a similar thread:
RecyclerView items are clickable but invisible
In my case, all the fonts and colors are correct. I know that the Loader is deprecated as of sdk 28. However, it's not even working in 27. Thank you in advance.
Activity where the data is added to the Content Provider:
public class StationListActivity extends AppCompatActivity implements StationsAdapter.StationsAdapterOnClickHandler, TubeStationAsyncTaskInterface,
LoaderManager.LoaderCallbacks<Cursor>
{
//Tag for the log messages
private static final String TAG = StationListActivity.class.getSimpleName();
#BindView(R.id.recyclerview_station)
RecyclerView mStationRecyclerView;
private StationsAdapter stationsAdapter;
private ArrayList<Stations> stationsArrayList = new ArrayList<>();
private static final String KEY_STATIONS_LIST = "stations_list";
private static final String KEY_LINE_NAME = "line_name";
Lines lines;
public String lineId;
private Context context;
private TextView lineNameStation;
private String lineNameToString;
#BindView(R.id.favorites_button)
Button favoritesButton;
/**
* Identifier for the favorites data loader
*/
private static final int FAVORITES_LOADER = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_station_list);
context = getApplicationContext();
// Bind the views
ButterKnife.bind(this);
stationsAdapter = new StationsAdapter(this, stationsArrayList, context);
mStationRecyclerView.setAdapter(stationsAdapter);
RecyclerView.LayoutManager mStationLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
mStationRecyclerView.setLayoutManager(mStationLayoutManager);
lineNameStation = (TextView) findViewById(R.id.line_name_station);
//add to favorites
favoritesButton.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View view)
{
ContentValues values = new ContentValues();
values.put(TubeLineContract.TubeLineEntry.COLUMN_LINES_ID, lines.getLineId());
values.put(TubeLineContract.TubeLineEntry.COLUMN_LINES_NAME, lines.getLineName());
Uri uri = getContentResolver().insert(TubeLineContract.TubeLineEntry.CONTENT_URI, values);
if (uri != null)
{
Toast.makeText(getBaseContext(), uri.toString(), Toast.LENGTH_LONG).show();
Toast.makeText(StationListActivity.this, R.string.favorites_added, Toast.LENGTH_SHORT).show();
favoritesButton.setVisibility(View.GONE);
}
}
});
/*
* Starting the asyncTask so that stations load when the activity opens.
*/
if (getIntent() != null && getIntent().getExtras() != null)
{
if (savedInstanceState == null)
{
lines = getIntent().getExtras().getParcelable("Lines");
lineId = lines.getLineId();
TubeStationAsyncTask myStationTask = new TubeStationAsyncTask(this);
myStationTask.execute(lineId);
lineNameStation.setText(lines.getLineName());
} else
{
stationsArrayList = savedInstanceState.getParcelableArrayList(KEY_STATIONS_LIST);
stationsAdapter.setStationsList(stationsArrayList);
}
}
// Kick off the loader
getLoaderManager().initLoader(FAVORITES_LOADER, null, this);
}
#Override
public void returnStationData(ArrayList<Stations> simpleJsonStationData) {
if (null != simpleJsonStationData) {
stationsAdapter = new StationsAdapter(this, simpleJsonStationData, StationListActivity.this);
stationsArrayList = simpleJsonStationData;
mStationRecyclerView.setAdapter(stationsAdapter);
stationsAdapter.setStationsList(stationsArrayList);
}
}
#Override
public void onClick(Stations stations) {
Intent intent = new Intent(StationListActivity.this, StationScheduleActivity.class);
intent.putExtra("Stations", stations);
intent.putExtra("Lines", lines);
startActivity(intent);
}
#Override
public Loader<Cursor> onCreateLoader(int loaderId, Bundle bundle)
{
String[] projection = {TubeLineContract.TubeLineEntry._ID, TubeLineContract.TubeLineEntry.COLUMN_LINES_ID,};
String[] selectionArgs = new String[]{lineId};
switch (loaderId)
{
case FAVORITES_LOADER:
return new CursorLoader(this, // Parent activity context
TubeLineContract.TubeLineEntry.CONTENT_URI, // Provider content URI to query
projection, // Columns to include in the resulting Cursor
TubeLineContract.TubeLineEntry.COLUMN_LINES_ID + "=?",
selectionArgs,
null); // Default sort order
default:
throw new RuntimeException("Loader Not Implemented: " + loaderId);
}
}
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor)
{
if ((cursor != null) && (cursor.getCount() > 0))
{
//"Add to Favorites" button is disabled in the StationList Activity when the user clicks on a line stored in Favorites
favoritesButton.setEnabled(false);
}
}
public void onLoaderReset(Loader<Cursor> cursorLoader)
{
}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putParcelableArrayList(KEY_STATIONS_LIST, stationsArrayList);
super.onSaveInstanceState(outState);
}
}
Activity with the menu option:
public class MainActivity extends AppCompatActivity implements LinesAdapter.LinesAdapterOnClickHandler, TubeLineAsyncTaskInterface,
LoaderManager.LoaderCallbacks<Cursor> {
// Tag for logging
private static final String TAG = MainActivity.class.getSimpleName();
#BindView(R.id.recyclerview_main)
RecyclerView mLineRecyclerView;
private LinesAdapter linesAdapter;
private ArrayList<Lines> linesArrayList = new ArrayList<>();
private Context context;
private static final String KEY_LINES_LIST = "lines_list";
CoordinatorLayout mCoordinatorLayout;
#BindView(R.id.pb_loading_indicator)
ProgressBar mLoadingIndicator;
private AdView adView;
private FavoritesAdapter favoritesAdapter;
private static final int FAVORITES_LOADER_ID = 0;
private int mPosition = RecyclerView.NO_POSITION;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = getApplicationContext();
// Bind the views
ButterKnife.bind(this);
mCoordinatorLayout = findViewById(R.id.coordinatorLayout);
favoritesAdapter = new FavoritesAdapter(this, context);
linesAdapter = new LinesAdapter(this, linesArrayList, context);
mLineRecyclerView.setAdapter(linesAdapter);
RecyclerView.LayoutManager mLineLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
mLineRecyclerView.setLayoutManager(mLineLayoutManager);
new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
#Override
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
if (viewHolder instanceof LinesAdapter.LinesAdapterViewHolder) return 0;
return super.getSwipeDirs(recyclerView, viewHolder);
}
// Called when a user swipes left or right on a ViewHolder
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
// Here is where you'll implement swipe to delete
//Construct the URI for the item to delete
//[Hint] Use getTag (from the adapter code) to get the id of the swiped item
// Retrieve the id of the task to delete
int id = (int) viewHolder.itemView.getTag();
// Build appropriate uri with String row id appended
String stringId = Integer.toString(id);
Uri uri = TubeLineContract.TubeLineEntry.CONTENT_URI;
uri = uri.buildUpon().appendPath(stringId).build();
// TODO (2) Delete a single row of data using a ContentResolver
int rowsDeleted = getContentResolver().delete(uri, null, null);
Log.v("CatalogActivity", rowsDeleted + " rows deleted from the movie database");
// TODO (3) Restart the loader to re-query for all tasks after a deletion
getSupportLoaderManager().restartLoader(FAVORITES_LOADER_ID, null, MainActivity.this);
}
}).attachToRecyclerView(mLineRecyclerView);
/*
* Starting the asyncTask so that lines load upon launching the app.
*/
if (savedInstanceState == null)
{
if (isNetworkStatusAvailable(this))
{
TubeLineAsyncTask myLineTask = new TubeLineAsyncTask(this);
myLineTask.execute(NetworkUtils.buildLineUrl());
} else {
Snackbar
.make(mCoordinatorLayout, "Please check your internet connection", Snackbar.LENGTH_INDEFINITE)
.setAction("Retry", new MyClickListener())
.show();
}
} else {
linesArrayList = savedInstanceState.getParcelableArrayList(KEY_LINES_LIST);
linesAdapter.setLinesList(linesArrayList);
}
getSupportLoaderManager().initLoader(FAVORITES_LOADER_ID, null, MainActivity.this);
mLineRecyclerView.setAdapter(favoritesAdapter);
}
public class MyClickListener implements View.OnClickListener {
#Override
public void onClick(View v) {
// Run the AsyncTask in response to the click
TubeLineAsyncTask myLineTask = new TubeLineAsyncTask(MainActivity.this);
myLineTask.execute();
}
}
#Override
public void returnLineData(ArrayList<Lines> simpleJsonLineData) {
mLoadingIndicator.setVisibility(View.INVISIBLE);
if (null != simpleJsonLineData) {
linesAdapter = new LinesAdapter(this, simpleJsonLineData, MainActivity.this);
linesArrayList = simpleJsonLineData;
mLineRecyclerView.setAdapter(linesAdapter);
linesAdapter.setLinesList(linesArrayList);
} else {
showErrorMessage();
}
}
#Override
public void onClick(Lines lines) {
Intent intent = new Intent(MainActivity.this, StationListActivity.class);
intent.putExtra("Lines", lines);
startActivity(intent);
}
//Display if there is no internet connection
public void showErrorMessage() {
Snackbar
.make(mCoordinatorLayout, "Please check your internet connection", Snackbar.LENGTH_INDEFINITE)
.setAction("Retry", new MyClickListener())
.show();
mLineRecyclerView.setVisibility(View.INVISIBLE);
mLoadingIndicator.setVisibility(View.VISIBLE);
}
public static boolean isNetworkStatusAvailable(Context context) {
ConnectivityManager cm =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
return activeNetwork != null &&
activeNetwork.isConnectedOrConnecting();
}
#Override
public Loader<Cursor> onCreateLoader(int id, final Bundle loaderArgs)
{
return new AsyncTaskLoader<Cursor>(this)
{
// Initialize a Cursor, this will hold all the task data
Cursor mFavoritesData = null;
// onStartLoading() is called when a loader first starts loading data
#Override
protected void onStartLoading()
{
if (mFavoritesData != null)
{
// Delivers any previously loaded data immediately
deliverResult(mFavoritesData);
}
else
{
// Force a new load
forceLoad();
}
}
// loadInBackground() performs asynchronous loading of data
#Override
public Cursor loadInBackground()
{
// Will implement to load data
// Query and load all task data in the background; sort by priority
// [Hint] use a try/catch block to catch any errors in loading data
try
{
return getContentResolver().query(TubeLineContract.TubeLineEntry.CONTENT_URI,
null,
null,
null,
TubeLineContract.TubeLineEntry.COLUMN_LINES_ID);
}
catch (Exception e)
{
Log.e(LOG_TAG, "Failed to asynchronously load data.");
e.printStackTrace();
return null;
}
}
// deliverResult sends the result of the load, a Cursor, to the registered listener
public void deliverResult(Cursor data)
{
mFavoritesData = data;
super.deliverResult(data);
}
};
}
/**
* Called when a previously created loader has finished its load.
*
* #param loader The Loader that has finished.
* #param data The data generated by the Loader.
*/
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data)
{
favoritesAdapter.swapCursor(data);
if (mPosition == RecyclerView.NO_POSITION) mPosition = 0;
mLineRecyclerView.smoothScrollToPosition(mPosition);
}
/**
* Called when a previously created loader is being reset, and thus
* making its data unavailable.
* onLoaderReset removes any references this activity had to the loader's data.
*
* #param loader The Loader that is being reset.
*/
#Override
public void onLoaderReset(Loader<Cursor> loader)
{
favoritesAdapter.swapCursor(null);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
/* Use AppCompatActivity's method getMenuInflater to get a handle on the menu inflater */
MenuInflater inflater = getMenuInflater();
/* Use the inflater's inflate method to inflate our menu layout to this menu */
inflater.inflate(R.menu.main, menu);
/* Return true so that the menu is displayed in the Toolbar */
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
TubeLineAsyncTask myLineTask = new TubeLineAsyncTask(this);
switch (item.getItemId())
{
case R.id.most_frequented_favorites:
getSupportLoaderManager().restartLoader(FAVORITES_LOADER_ID, null, MainActivity.this);
favoritesAdapter = new FavoritesAdapter(this, MainActivity.this);
mLineRecyclerView.setAdapter(favoritesAdapter);
return true;
case R.id.line_list:
myLineTask.execute();
return true;
default:
return super.onOptionsItemSelected(item);
}}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putParcelableArrayList(KEY_LINES_LIST, linesArrayList);
super.onSaveInstanceState(outState);
}
}
RecyclerViewAdapter class:
public class FavoritesAdapter extends RecyclerView.Adapter<FavoritesAdapter.FavoritesAdapterViewHolder>
{
private static final String TAG = FavoritesAdapter.class.getSimpleName();
private Context context;
private Cursor cursor;
private LinesAdapter.LinesAdapterOnClickHandler mClickHandler;
public FavoritesAdapter(LinesAdapter.LinesAdapterOnClickHandler clickHandler, Context context)
{
mClickHandler = clickHandler;
this.context = context;
}
public class FavoritesAdapterViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
#BindView(R.id.line_name)
TextView lineName;
public FavoritesAdapterViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
view.setOnClickListener(this);
}
#Override
public void onClick(View v) {
cursor.moveToPosition(getAdapterPosition());
String lineName = cursor.getString(cursor.getColumnIndexOrThrow(TubeLineContract.TubeLineEntry.COLUMN_LINES_NAME));
String lineId = cursor.getString(cursor.getColumnIndexOrThrow(TubeLineContract.TubeLineEntry.COLUMN_LINES_ID));
Lines line = new Lines(lineName, lineId);
mClickHandler.onClick(line);
}
}
#Override
public void onBindViewHolder(FavoritesAdapter.FavoritesAdapterViewHolder holder, int position)
{
// get to the right location in the cursor
cursor.moveToPosition(position);
// Determine the values of the wanted data
int lineIdIndex = cursor.getColumnIndexOrThrow(TubeLineContract.TubeLineEntry.COLUMN_LINES_ID);
final int id = cursor.getInt(lineIdIndex);
holder.itemView.setTag(id);
}
#Override
public FavoritesAdapter.FavoritesAdapterViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType)
{
Context context = viewGroup.getContext();
int layoutIdForListItem = R.layout.line_list_item;
LayoutInflater inflater = LayoutInflater.from(context);
boolean shouldAttachToParentImmediately = false;
View view = inflater.inflate(layoutIdForListItem, viewGroup, shouldAttachToParentImmediately);
return new FavoritesAdapter.FavoritesAdapterViewHolder(view);
}
public Cursor swapCursor(Cursor c)
{
// check if this cursor is the same as the previous cursor (mCursor)
if (cursor == c)
{
return null; // bc nothing has changed
}
Cursor temp = cursor;
this.cursor = c; // new cursor value assigned
//check if this is a valid cursor, then update the cursor
if (c != null)
{
this.notifyDataSetChanged();
}
return temp;
}
#Override
public int getItemCount()
{
if (null == cursor)
return 0;
return cursor.getCount();
}
}
I'm new in Android and I have following code that shows the list of item in Adapter.
I have Four Different Adapter from where I am calling one comman AsyncTask to update Result. I have implemented one Interface ApiResponse and overrides apiResponseProcessing() to get result.
In Item of List "Add to Cart" Button Added in every row. OnClick of that button I am requesting to server. On Success of that response i want to update Button with "Added To Cart".
I have question How to update that string which is binded in onBindViewHolder(). I am getting success in that method but dont know how to update clicked Button from that method.
Here's my Adapter
/**
* Adapter
**/
public class AlbumPhotoDetailAdapter
extends RecyclerView.Adapter<AlbumPhotoDetailAdapter.ViewHolder> implements ApiResponse {
private final ArrayList<Photo> mValues;
Album album;
private Activity mContext;
private int mMemberId;
public AlbumPhotoDetailAdapter(Activity context, ArrayList<Photo> items) {
mValues = items;
this.mContext = context;
mMemberId = MemberPreference.getMemberId(mContext);
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.album_photo_detail_sub_view, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final Photo photo = mValues.get(position);
/**
* Album Owner Name
*/
String mOwnerName = photo.getOwnerName();
String mOwnerProfilePic = photo.getOwnerImage();
String mDateTime = photo.getDatetime();
String mPrice = String.valueOf(photo.getPrice());
/**
* Price String
*/
String priceStr = String.format(mContext.getString(R.string.string_dollar_price), mPrice);
holder.mAlbumPhotoDetailPhotoPrice.setText(priceStr);
/**
* Main Image
*/
Picasso.with(mContext).load(photo.getLink())
.error(R.drawable.ic_place_holder_circle)
.placeholder(R.drawable.ic_place_holder_circle)
.transform(new ImageTransformation(holder.mAlbumPhotoDetailSubMainImage))
.into(holder.mAlbumPhotoDetailSubMainImage);
/**
* Owner Name and Profile Pic
*/
holder.mAlbumPhotoDetailSubOwnerNameTextView.setText(mOwnerName);
Picasso.with(mContext).load(mOwnerProfilePic)
.error(R.drawable.ic_place_holder_circle)
.placeholder(R.drawable.ic_place_holder_circle)
.resize(100, 100)
.transform(new CircleTransform())
.into(holder.mAlbumPhotoDetailSubOwnerImage);
mDateTime = mDateTime != null ? DateUtils.getNiceTime(mDateTime) : "----";
holder.mAlbumPhotoDetailSubOwnerPostedTimeTextView.setText(mDateTime);
// Photo Add to cart.
holder.mAddToCartButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(InternetConnection.checkConnection(mContext)) {
new BackgroundAsyncTask(mContext, (ApiResponse) mContext, mMemberId, photo.getId()).execute();
} else {
DailyStudio.noInternetConnectionToast(mContext);
}
}
});
}
#Override
public int getItemCount() {
return mValues.size();
}
#Override
public void apiResponseProcessing(String response) {
Log.i(TAG,"Api Response : "+response);
if(response.equals(Fields.JSON_SUCCESS)) {
}
}
/**
* View Holder
*/
public static class ViewHolder extends RecyclerView.ViewHolder {
public final View mView;
private ImageView mAlbumPhotoDetailSubOwnerImage;
private ImageView mAlbumPhotoDetailSubMainImage;
private TextView mAlbumPhotoDetailSubOwnerNameTextView;
private TextView mAlbumPhotoDetailSubOwnerPostedTimeTextView;
private TextView mAlbumPhotoDetailPhotoPrice;
private TextView mAlbumPhotoDetailSubDescription;
private Button mAddToCartButton;
public ViewHolder(View view) {
super(view);
mView = view;
mAlbumPhotoDetailSubOwnerImage = (ImageView) view.findViewById(R.id.album_photo_detail_sub_owner_image);
mAlbumPhotoDetailSubMainImage = (ImageView) view.findViewById(R.id.album_photo_detail_sub_main_image);
mAlbumPhotoDetailSubOwnerNameTextView = (TextView) view.findViewById(R.id.album_photo_detail_sub_owner_name_text_view);
mAlbumPhotoDetailSubOwnerPostedTimeTextView = (TextView) view.findViewById(R.id.album_photo_detail_sub_owner_posted_time_text_view);
mAlbumPhotoDetailPhotoPrice = (TextView) view.findViewById(R.id.album_photo_detail_photo_price);
mAlbumPhotoDetailSubDescription = (TextView) view.findViewById(R.id.album_photo_detail_sub_description);
mAddToCartButton = (Button) view.findViewById(R.id.album_photo_detail_photo_add_to_cart_button);
}
}
}
Here's my Interface
/**
* Interface..
*/
public interface ApiResponse {
public void apiResponseProcessing(String response);
}
Here's my Background AsyncTask
/**
* Background AsyncTask...
*/
public class BackgroundAsyncTask extends AsyncTask<Void, Void, String> {
private Context context;
private String accessToken;
private int memberId;
private int photoId;
private ApiResponse objIBaseApi;
public BackgroundAsyncTask(Context context, ApiResponse apiResponse, int memberId, int photoId) {
this.context = context;
this.memberId = memberId;
this.photoId = photoId;
accessToken = MemberPreference.getAccessToken(context);
this.objIBaseApi = apiResponse;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected String doInBackground(Void... params) {
JSONObject json = JSONParser.addToCartPhoto(accessToken, memberId, photoId);
if(json != null) {
Log.i(TAG,"First Json : "+json.toString());
try {
if (json.getString(Fields.RESULT).equalsIgnoreCase(Fields.JSON_SUCCESS)) {
return Fields.JSON_SUCCESS;
} else if(json.getString(Fields.JSON_ERROR).equalsIgnoreCase(Fields.ERROR_ACCESS_DENIED)) {
String refreshToken = MemberPreference.getRefreshToken(context);
JSONObject newJSONObject = JSONParser.loginMemberWithRefreshToken(refreshToken, Integer.toString(memberId));
if(newJSONObject != null) {
if(newJSONObject.getString(Fields.JSON_ERROR).equalsIgnoreCase(Fields.ERROR_ACCESS_DENIED)) {
return Fields.ERROR_ACCESS_DENIED;
} else {
return Fields.JSON_SUCCESS;
}
} else
return Fields.ERROR_ACCESS_DENIED;
} else {
return Fields.JSON_ERROR;
}
} catch (JSONException e) {
e.printStackTrace();
return Fields.JSON_ERROR;
}
}
return Fields.JSON_ERROR;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
objIBaseApi.apiResponseProcessing(result);
}
}
Is there any solution or better way to do like this?
Your help would be appreciated. Thank you.
You Can keep one flag isAddedToCart variable in you bean class which you are using in your adapter(Photo). Now just pass the position in your asynctask once user click on "add to cart" button. On getting the successful you just need to find the bean from the list of bean you passed in adapter and change the flag isAddedToCart to true and notify your adapter thats it. Here is the code snippet:-
Photo Class
public class Photo{
private boolean isAddedToCart;
public void setAddedTOCart(boolean isAdded){
isAddedToCart = isAdded;
}
public boolean isAddedToCart(){
return isAddedToCart;
}
}
AlbumPhotoDetailAdapter onBindViewHolder
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final Photo photo = mValues.get(position);
/**
* Album Owner Name
*/
String mOwnerName = photo.getOwnerName();
String mOwnerProfilePic = photo.getOwnerImage();
String mDateTime = photo.getDatetime();
String mPrice = String.valueOf(photo.getPrice());
String isAdded = photo.isAddedToCart();
/**
* Price String
*/
String priceStr = String.format(mContext.getString(R.string.string_dollar_price), mPrice);
holder.mAlbumPhotoDetailPhotoPrice.setText(priceStr);
/**
* Main Image
*/
Picasso.with(mContext).load(photo.getLink())
.error(R.drawable.ic_place_holder_circle)
.placeholder(R.drawable.ic_place_holder_circle)
.transform(new ImageTransformation(holder.mAlbumPhotoDetailSubMainImage))
.into(holder.mAlbumPhotoDetailSubMainImage);
/**
* Owner Name and Profile Pic
*/
holder.mAlbumPhotoDetailSubOwnerNameTextView.setText(mOwnerName);
Picasso.with(mContext).load(mOwnerProfilePic)
.error(R.drawable.ic_place_holder_circle)
.placeholder(R.drawable.ic_place_holder_circle)
.resize(100, 100)
.transform(new CircleTransform())
.into(holder.mAlbumPhotoDetailSubOwnerImage);
mDateTime = mDateTime != null ? DateUtils.getNiceTime(mDateTime) : "----";
holder.mAlbumPhotoDetailSubOwnerPostedTimeTextView.setText(mDateTime);
if(isAdded){
holder.mAddToCartButton.setText("Added TO Cart");
}else{
holder.mAddToCartButton.setText("Add TO Cart");
}
// Photo Add to cart.
holder.mAddToCartButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(InternetConnection.checkConnection(mContext)) {
new BackgroundAsyncTask(mContext, (ApiResponse) mContext, mMemberId, photo.getId(),position).execute();
} else {
DailyStudio.noInternetConnectionToast(mContext);
}
}
});
}
your Interface
public interface ApiResponse {
public void apiResponseProcessing(String response,int position);
}
Your Adapter apiResponceProcessing()
#Override
public void apiResponseProcessing(String response,int position) {
Log.i(TAG,"Api Response : "+response);
if(response.equals(Fields.JSON_SUCCESS)) {
mValues.get(position).setAddedTOCart(true);
notifyDataSetChange();
}
}
And finally your
BackgroundAsyncTask
public class BackgroundAsyncTask extends AsyncTask<Void, Void, String> {
private Context context;
private String accessToken;
private int memberId;
private int photoId;
private int mPosition;
private ApiResponse objIBaseApi;
public BackgroundAsyncTask(Context context, ApiResponse apiResponse, int memberId, int photoId,int position) {
this.context = context;
this.memberId = memberId;
this.photoId = photoId;
accessToken = MemberPreference.getAccessToken(context);
this.objIBaseApi = apiResponse;
this.mPosition = position;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected String doInBackground(Void... params) {
JSONObject json = JSONParser.addToCartPhoto(accessToken, memberId, photoId);
if(json != null) {
Log.i(TAG,"First Json : "+json.toString());
try {
if (json.getString(Fields.RESULT).equalsIgnoreCase(Fields.JSON_SUCCESS)) {
return Fields.JSON_SUCCESS;
} else if(json.getString(Fields.JSON_ERROR).equalsIgnoreCase(Fields.ERROR_ACCESS_DENIED)) {
String refreshToken = MemberPreference.getRefreshToken(context);
JSONObject newJSONObject = JSONParser.loginMemberWithRefreshToken(refreshToken, Integer.toString(memberId));
if(newJSONObject != null) {
if(newJSONObject.getString(Fields.JSON_ERROR).equalsIgnoreCase(Fields.ERROR_ACCESS_DENIED)) {
return Fields.ERROR_ACCESS_DENIED;
} else {
return Fields.JSON_SUCCESS;
}
} else
return Fields.ERROR_ACCESS_DENIED;
} else {
return Fields.JSON_ERROR;
}
} catch (JSONException e) {
e.printStackTrace();
return Fields.JSON_ERROR;
}
}
return Fields.JSON_ERROR;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
objIBaseApi.apiResponseProcessing(result,mPosition);
}
}
Firstly in my opinion adapter should not care about network request. But
giving an answer in substance, you can try pass anonymous class for your apiResponseProcessing in same manner as you create OnClickListener for your button. It can look like this:
holder.mAddToCartButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(InternetConnection.checkConnection(mContext)) {
new BackgroundAsyncTask(
mContext,
new ApiResponse() {
#Override
public void apiResponseProcessing(String response) {
Log.i(TAG,"Api Response : "+response);
if(response.equals(Fields.JSON_SUCCESS)) {
// Here you can access you holder till it final
}
}
},
mMemberId,
photo.getId()).execute();
} else {
DailyStudio.noInternetConnectionToast(mContext);
}
}
});
But code like this looks messy and spaghetti. As i say at the beginning there are exist at least one different approach to handle changes for buttons inside listview/recivleview. I use method, where adapter only care about building interface with given data and delegate buttons clicks to someone else (in most cases activity that contains listview). An easy way notify activity about button click is Bus messaging pattern. I use Otto event library. When delegate receive notification about button click, it can initiate data changing according current task and then initiate listview reloading or partial update only required rows.
Additional comments
Try to write beautiful code. Constructor AlbumPhotoDetailAdapter has different syntax to assign instance variables. One with this keyword and other without. Usually you should use one way.
public AlbumPhotoDetailAdapter(Activity context, ArrayList<Photo> items) {
this.values = items;
this.context = context;
this.memberId = MemberPreference.getMemberId(context);
}
album instance variable have no access modifiers indication. You should know, that in java programming language omitting access specifiers is not the same as private modifier.
How can I refresh the view of a fragment, when the back button is pressed?
I have tried this in the onResume method of the fragment but it doesn't work.
OK, here is the code
#SuppressWarnings("unused")
public class RestaurantMenuFragment extends Fragment {
private static final String TAG = "MenuItemsFragment";
private static final String CATEGORIES_KEY = "categories";
private static final String SELECTED_CATEGORY_ID_KEY = "category";
private static final String RESTAURANT_KEY = "restaurant123";
private static final String RESTAURANT_KCITY = "city";
private Spinner mCategoriesSpinner;
private ArrayAdapter<CategoriesResponse.Category> mCategoriesAdapter;
private ListView mListView;
private List<MenuItem> mItems;
private MenuItemsAdapter mItemsAdapter;
private EmptyLayout mEmptyLayout;
private Restaurant mRestaurant;
private int mCategoryId;
private List<CategoriesResponse.Category> mCategories;
private RestaurantActivity mActivity;
private MainApplication mApplication;
private CategoriesResponse mCategoriesResponse;
private ActionBar mActionBar;
private Gson mGson;
int categ;
private ObjectGetter mObjectGetter;
public static RestaurantMenuFragment newInstance(Restaurant restaurant) {
RestaurantMenuFragment fragment = new RestaurantMenuFragment();
Bundle args = new Bundle();
args.putString(RESTAURANT_KEY, new Gson().toJson(restaurant));
String dd=restaurant.city;
Log.i("dd12", dd);
fragment.setArguments(args);
return fragment;
}
public RestaurantMenuFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mActivity = (RestaurantActivity) getActivity();
mApplication = (MainApplication) mActivity.getApplication();
mActionBar = mActivity.getSupportActionBar();
mGson = new Gson();
mObjectGetter = new ObjectGetter();
mCategories = new ArrayList<CategoriesResponse.Category>();
Log.i("mCategories",""+mCategories);
mItems = new ArrayList<MenuItem>();
Log.i("12345",""+mItems);
mItemsAdapter = new MenuItemsAdapter(getActivity(), mItems);
Bundle args = getArguments();
if (args != null) {
mRestaurant = mGson.fromJson(args.getString(RESTAURANT_KEY),
Restaurant.class);
}
if (savedInstanceState != null) {
mRestaurant = mGson.fromJson(
savedInstanceState.getString(RESTAURANT_KEY),
Restaurant.class);
mCategoryId = savedInstanceState.getInt(SELECTED_CATEGORY_ID_KEY);
mCategoriesResponse = mGson.fromJson(
savedInstanceState.getString(CATEGORIES_KEY),
CategoriesResponse.class);
}
assert mRestaurant != null;
updateCart();
}
public void updateCart() {
View view = mActionBar.getCustomView();
Button cartButton = (Button) view.findViewById(R.id.cartButton);
int nOfItems = 0;
if (mApplication.isCartCreated()) {
nOfItems = mApplication.getCart().getNOfAllItems();
}
cartButton.setText(String.format("%d", nOfItems));
if (nOfItems > 0) {
cartButton.setEnabled(true);
} else {
cartButton.setEnabled(false);
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Gson gson = new Gson();
outState.putString(RESTAURANT_KEY, gson.toJson(mRestaurant));
outState.putInt(SELECTED_CATEGORY_ID_KEY, mCategoryId);
outState.putString(CATEGORIES_KEY, gson.toJson(mCategoriesResponse));
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onViewCreated(view, savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.spinner_list, container, false);
RestaurantActivity activity = (RestaurantActivity) getActivity();
String myDataFromActivity = activity.getMyData();
String myDataFromActivity1 = activity.getMyData1();
Log.i("myDataFromActivity",myDataFromActivity);
Log.i("myDataFromActivity1",myDataFromActivity1);
categ=Integer.parseInt(myDataFromActivity1);
mListView = (ListView) view.findViewById(R.id.list122334);
mListView.setAdapter(mItemsAdapter);
Log.d(TAG,"Querying items url "
+ Urls.menuItemsQuery(mRestaurant.id,categ));
mEmptyLayout = EmptyLayout.with(getActivity()).to(mListView)
.setEmptyMessage(R.string.categories_empty_message)
.showLoading();
loadItems();
return view;
}
private void loadItems() {
mEmptyLayout.showLoading();
mItems.clear();
mObjectGetter.getJsonObjectOrDialog(mActivity,
Urls.menuItemsQuery(mRestaurant.id, categ),
ItemsResponse.class,
new ObjectGetter.OnFinishedListener<ItemsResponse>() {
#Override
public void onFinishedLoadingObject(
ItemsResponse itemsResponse) {
mEmptyLayout.showEmpty();
if (itemsResponse != null
&& itemsResponse.items != null) {
mItems.addAll(itemsResponse.items);
}
mItemsAdapter.notifyDataSetChanged();
}
});
}
private class MenuItemsAdapter extends ArrayAdapter<MenuItem> {
private static final String TAG = "MenuItemsAdapter";
public MenuItemsAdapter(Context context, List<MenuItem> menuItems) {
super(context, 0, menuItems);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final MenuItem menuItem = getItem(position);
View view = convertView;
final ViewHolder viewHolder;
LayoutInflater inflater;
if (convertView == null) {
inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.menu_item, parent, false);
viewHolder = new ViewHolder();
viewHolder.name = (TextView) view.findViewById(R.id.name);
viewHolder.description = (TextView) view.findViewById(R.id.description);
viewHolder.price = (TextView) view.findViewById(R.id.price);
viewHolder.add = (Button) view.findViewById(R.id.add);
viewHolder.selectedView = view.findViewById(R.id.selectedView);
viewHolder.remove = (Button) view.findViewById(R.id.remove);
viewHolder.total = (TextView) view.findViewById(R.id.itemTotal);
viewHolder.quantity = (TextView) view.findViewById(R.id.quantity);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
try
{
viewHolder.name.setText(menuItem.name);
viewHolder.description.setText(menuItem.description);
viewHolder.price.setText(String.valueOf(menuItem.price));
}catch(NullPointerException e){
e.printStackTrace();
}
viewHolder.add.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mApplication.createNewCartIfPossibleAndAskIfNot(
getActivity(), mRestaurant,
new MainApplication.OnCreateCartListener() {
#Override
public void onCreateCart(Cart cart) {
cart.addOne(menuItem);
updateItemFromCart(menuItem, viewHolder);
updateCart();
}
});
}
});
viewHolder.remove.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!mApplication.isCartCreated()) {
return;
}
mApplication.getCart().removeOne(menuItem);
updateItemFromCart(menuItem, viewHolder);
updateCart();
}
});
return view;
}
private void updateItemFromCart(MenuItem menuItem, ViewHolder viewHolder) {
if (!mApplication.isCartCreated()) {
return;
}
int quantity = mApplication.getCart().getNOfItemsOfType(menuItem);
if (quantity > 0) {
viewHolder.selectedView.setVisibility(View.VISIBLE);
} else {
viewHolder.selectedView.setVisibility(View.GONE);
}
viewHolder.quantity.setText(String.valueOf(quantity));
viewHolder.total.setText(String.valueOf(quantity
* menuItem.price));
}
class ViewHolder {
TextView name;
TextView description;
TextView price;
Button add;
View selectedView;
Button remove;
TextView total;
TextView quantity;
}
}
#Override
public void onResume() {
super.onResume();
updateCart();
mItems.clear();
if (mItemsAdapter != null) {
mItemsAdapter.notifyDataSetChanged();
}
}
#Override
public void onDestroy() {
if (mObjectGetter != null) {
mObjectGetter.stopRequests();
}
super.onDestroy();
}
}
Now, i want to update the listvieww data when the user pressed the back button. I set the new loadItems() method in the onResume() Method of the Fragment. This Method is called but the old listview data appears and new data also appears...
Back button should be handled from Activity.
You can override onBackPressed in Activity and call a function on corresponding fragment to reloadItems().
Here are your 3 options I could think of.
Get reference to Fragment and call function to reLoadItems and its better to define an interface for this communication which fragment implements.
Better solution than first one. Add a LocalBroadcast which Activity broadcasts and your fragment listens and updates data on receiving broadcast.
Example for this :
http://luboganev.github.io/blog/messaging-with-localbroadcastmanager/
Otto event bus where both activity and fragment classes are connected to the event bus and they activity publishes event and fragment subscribes to it. This is what I am using for something similar in my application. (But I have pretty frequent asynchronous events that come along. SO I am using this. 2nd option might be sufficient in your case).
Example for this :
http://www.vogella.com/tutorials/JavaLibrary-EventBusOtto/article.html
As ramesh already mentioned, back button handling happens in your activity class that holds the fragments. Here is a simple example, how you can handle these back button events for your fragment.
Activity Code:
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
boolean returnSuperKeyDown = true;
if(keyCode == KeyEvent.KEYCODE_BACK){
Fragment fragment = getYourCurrentFragment();
if (fragment instanceof YourFragment) {
returnSuperKeyDown = ((YourFragment) fragment).onFragmentKeyDown();
}
}
if (returnSuperKeyDown) {
return super.onKeyDown(keyCode, event);
} else {
return true;
}
}
YourFragment Method:
public boolean onFragmentKeyDown() {
updateYourFragment();
return false;
}
#Rithe, #sunder sharma
As per me there is simple to refresh the fragment when come back from other fragment,
We just have to override the onActivityCreated Method for refresh fragment.
Like as
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//your code which you want to refresh
loadItems();
}
You can also update/refresh the fragment using onStart() method.
public void onStart(){
super.onStart();
//update your fragment
}
This worked fine for me.
call your loadItem() method onHiddenChanged(boolean hidden)method.onHiddenChanged is overrided method
I can show images in gridview normally. However, I want to use pulltorefresh functionality and I found a library to get the functionality. I am confused how to integrate my images.
Now, the gridview just shows imagelinks in the array. How can I use imageAdapter instead of arrayAdapter to show the Images?
Thanks a lot.
This is the activity class:
public final class PullToRefreshGridActivity extends Activity {
static final int MENU_SET_MODE = 0;
private LinkedList<String> mListItems;
private PullToRefreshGridView mPullRefreshGridView;
private GridView mGridView;
private ArrayAdapter<String> mAdapter;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ptr_grid);
mPullRefreshGridView = (PullToRefreshGridView) findViewById(R.id.pull_refresh_grid);
mGridView = mPullRefreshGridView.getRefreshableView();
// Set a listener to be invoked when the list should be refreshed.
mPullRefreshGridView.setOnRefreshListener(new OnRefreshListener2<GridView>() {
#Override
public void onPullDownToRefresh(PullToRefreshBase<GridView> refreshView) {
Toast.makeText(PullToRefreshGridActivity.this, "Pull Down!", Toast.LENGTH_SHORT).show();
new GetDataTask().execute();
}
#Override
public void onPullUpToRefresh(PullToRefreshBase<GridView> refreshView) {
Toast.makeText(PullToRefreshGridActivity.this, "Pull Up!", Toast.LENGTH_SHORT).show();
new GetDataTask().execute();
}
});
mListItems = new LinkedList<String>();
TextView tv = new TextView(this);
tv.setGravity(Gravity.CENTER);
tv.setText("Empty View, Pull Down/Up to Add Items");
mPullRefreshGridView.setEmptyView(tv);
mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mListItems);
mGridView.setAdapter(mAdapter);
}
private class GetDataTask extends AsyncTask<Void, Void, String[]> {
#Override
protected String[] doInBackground(Void... params) {
// Simulates a background job.
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
return mStrings;
}
#Override
protected void onPostExecute(String[] result) {
mListItems.addFirst("Added after refresh...");
mListItems.addAll(Arrays.asList(result));
mAdapter.notifyDataSetChanged();
// Call onRefreshComplete when the list has been refreshed.
mPullRefreshGridView.onRefreshComplete();
super.onPostExecute(result);
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, MENU_SET_MODE, 0,
mPullRefreshGridView.getMode() == Mode.BOTH ? "Change to MODE_PULL_DOWN"
: "Change to MODE_PULL_BOTH");
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem setModeItem = menu.findItem(MENU_SET_MODE);
setModeItem.setTitle(mPullRefreshGridView.getMode() == Mode.BOTH ? "Change to MODE_PULL_FROM_START"
: "Change to MODE_PULL_BOTH");
return super.onPrepareOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_SET_MODE:
mPullRefreshGridView
.setMode(mPullRefreshGridView.getMode() == Mode.BOTH ? Mode.PULL_FROM_START
: Mode.BOTH);
break;
}
return super.onOptionsItemSelected(item);
}
private String[] mStrings = {"https://lh6.googleusercontent.com/-55osAWw3x0Q/URquUtcFr5I/AAAAAAAAAbs/rWlj1RUKrYI/s1024/A%252520Photographer.jpg",
"https://lh4.googleusercontent.com/--dq8niRp7W4/URquVgmXvgI/AAAAAAAAAbs/-gnuLQfNnBA/s1024/A%252520Song%252520of%252520Ice%252520and%252520Fire.jpg",
"https://lh5.googleusercontent.com/-7qZeDtRKFKc/URquWZT1gOI/AAAAAAAAAbs/hqWgteyNXsg/s1024/Another%252520Rockaway%252520Sunset.jpg",
"https://lh3.googleusercontent.com/--L0Km39l5J8/URquXHGcdNI/AAAAAAAAAbs/3ZrSJNrSomQ/s1024/Antelope%252520Butte.jpg",
"https://lh6.googleusercontent.com/-8HO-4vIFnlw/URquZnsFgtI/AAAAAAAAAbs/WT8jViTF7vw/s1024/Antelope%252520Hallway.jpg",
"https://lh4.googleusercontent.com/-WIuWgVcU3Qw/URqubRVcj4I/AAAAAAAAAbs/YvbwgGjwdIQ/s1024/Antelope%252520Walls.jpg",
"https://lh6.googleusercontent.com/-UBmLbPELvoQ/URqucCdv0kI/AAAAAAAAAbs/IdNhr2VQoQs/s1024/Apre%2525CC%252580s%252520la%252520Pluie.jpg",
"https://lh3.googleusercontent.com/-s-AFpvgSeew/URquc6dF-JI/AAAAAAAAAbs/Mt3xNGRUd68/s1024/Backlit%252520Cloud.jpg",
"https://lh5.googleusercontent.com/-bvmif9a9YOQ/URquea3heHI/AAAAAAAAAbs/rcr6wyeQtAo/s1024/Bee%252520and%252520Flower.jpg",
"https://lh5.googleusercontent.com/-n7mdm7I7FGs/URqueT_BT-I/AAAAAAAAAbs/9MYmXlmpSAo/s1024/Bonzai%252520Rock%252520Sunset.jpg",
"https://lh6.googleusercontent.com/-4CN4X4t0M1k/URqufPozWzI/AAAAAAAAAbs/8wK41lg1KPs/s1024/Caterpillar.jpg",
"https://lh3.googleusercontent.com/-rrFnVC8xQEg/URqufdrLBaI/AAAAAAAAAbs/s69WYy_fl1E/s1024/Chess.jpg",
"https://lh5.googleusercontent.com/-WVpRptWH8Yw/URqugh-QmDI/AAAAAAAAAbs/E-MgBgtlUWU/s1024/Chihuly.jpg",};
}
Create a custom Adapter, where you refresh each cell. Create an object which contains a String and Image property, in GetDataTask create an ArrayList of all those new objects and refresh your adapter.
Heres a simple example of the adapter and object
public class CustomStepsAdapter extends BaseAdapter {
private Context mContext;
private ArrayList<CustomObject> details;
private LayoutInflater mInflater;
public CustomStepsAdapter(Context aContext, ArrayList<CustomObject> data) {
this.details = data;
this.mContext = aContext;
mInflater = LayoutInflater.from(aContext);
}
public int getCount() {
return details.size();
}
public CustomObject getItem(int position) {
return details.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView() {
return mInflater.inflate(R.layout.custom_step_options_cell, null);
}
public View getView(int position, View convertView, ViewGroup parent) {
final CustomObject item = getItem(position);
CustomStepCell cell = null;
if (convertView == null) {
convertView = getView();
cell = new CustomStepCell();
cell.itemName = (TextView) convertView.findViewById(R.id.option_name);
cell.image = (ImageView) convertView.findViewById(R.id.green_checkmark);
convertView.setTag(cell);
}
else
{
cell = (CustomStepCell) convertView.getTag();
}
cell.itemName.setText(item.getItemName());
cell.image = item.getImage();
return convertView;
}
public boolean isShowPrices() {
return showPrices;
}
public void setShowPrices(boolean showPrices) {
this.showPrices = showPrices;
}
public static class CustomStepCell
{
public TextView itemName;
public ImageView image;
}
}
public class CustomObject
{
private String itemName;
private ImageView image;
public CustomObject()
{
this.itemName = "";
}
public void setImage(ImageView anImage)
{
this.image = anImage;
}
public void setItemName(String anItemName)
{
this.itemName = anItemName;
}
public ImageView getImage()
{
return this.image;
}
public String getItemName()
{
return this.itemName;
}
}
On my app I have a ListView with Checkboxes and have an adapter that extends BaseAdapterto populate the ListView. The basic function of the list is to show debts and the user can choose which ones to pay by checking the boxes, you have the total at the bottom of the list that gets updated when the user adds/removes an item. Now, some debts are related to another and if the user checks one, any other related debt should be marked as well and same if you uncheck a debt. I also have a button that should clear all the selected debts on the list, and that's where my problem is.
I keep record of the selected debts on an ArrayList and it seems to work for all the program but the clear button. When the button is pressed the selected debts list seems to be always empty. Any idea on what could be happening?
Here is my adapter:
public class ServicesFinancialStatusAdapter extends BaseAdapter implements CompoundButton.OnCheckedChangeListener{
private Context context;
private List<Debts> debtsList;
private List<Debts> selectedDebts;
private LayoutInflater inflater;
private int tabPosition;
private float total;
private OnTotalChangedListener listener;
public ServicesFinancialStatusAdapter(Context context, List<Debts> debtsList, int tabPosition) {
this.context = context;
this.debtsList = debtsList;
this.tabPosition = tabPosition;
this.total = 0;
this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.selectedDebts = new ArrayList<Debts>();
}
#Override
public int getCount() {
return debtsList.size();
}
#Override
public Object getItem(int i) {
return debtsList.get(i);
}
#Override
public long getItemId(int i) {
return i;
}
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder holder;
if (view == null) {
holder = new ViewHolder();
view = inflater.inflate(R.layout.item_services_financial_status, viewGroup, false);
holder.concept = (TextView) view.findViewById(R.id.payment_concept);
holder.descriptionOrDate = (TextView) view.findViewById(R.id.payment_description_date);
holder.amount = (TextView) view.findViewById(R.id.payment_amount);
holder.expirationDate = (TextView) view.findViewById(R.id.payment_expiration_date);
if (tabPosition > 4) {
holder.checkBox = (CheckBox) view.findViewById(R.id.check_box);
holder.checkBox.setOnCheckedChangeListener(this);
}
view.setTag(holder);
} else
holder = (ViewHolder) view.getTag();
Debts item = debtsList.get(i);
holder.concept.setText(item.getConcept());
holder.amount.setText(item.getAmountToString());
if (item.isExpired())
holder.expirationDate.setText(context.getString(R.string.expired_status_indicator));
else
holder.expirationDate.setText(context.getString(R.string.debts_expiration_date_indicator) + item.getExpirationDate());
if (tabPosition < 3)
holder.descriptionOrDate.setText(item.getDescription());
else if (tabPosition < 5)
holder.descriptionOrDate.setText(item.getDate());
else {
holder.descriptionOrDate.setVisibility(View.GONE);
holder.checkBox.setVisibility(View.VISIBLE);
holder.checkBox.setTag(i);
holder.checkBox.setChecked(item.isSelected());
}
return view;
}
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
//Get the position of the item clicked and the data object
Integer position = (Integer) buttonView.getTag();
Debts item = debtsList.get(position);
//Change the status ob the object
item.setSelected(isChecked);
for (Debts debts : debtsList) {
//Check on the list for related objects and marks them as well
if (debts.getConceptId() == item.getRelatedDebt())
debts.setSelected(isChecked);
//Get the amount of the debt and add/remove it from
//the selectedDebts list and update the total
float amount = debts.getAmount();
if (debts.isSelected()) {
if (!selectedDebts.contains(debts)) {
selectedDebts.add(debts);
listener.onTotalChanged(addToTotal(amount), selectedDebts);
}
}
else {
if (selectedDebts.contains(debts)) {
selectedDebts.remove(debts);
listener.onTotalChanged(removeFromTotal(amount), selectedDebts);
}
}
}
//Finally update the UI
notifyDataSetChanged();
}
//Anywhere else in the code selectedDebts has the right data but here
//Here the size of the list is always 0
public void unMarkDebts() {
for (Debts debts : debtsList) {
//Get the amount of the debt and remove it from
//the selectedDebts list, set the data object as unselected
//and update the total
float amount = debts.getAmount();
if (selectedDebts.contains(debts)) {
debts.setSelected(false);
selectedDebts.remove(debts);
listener.onTotalChanged(removeFromTotal(amount), selectedDebts);
}
}
//Update the UI
notifyDataSetChanged();
}
private float addToTotal(float value) {
return total += value;
}
private float removeFromTotal(float value) {
return total -=value;
}
public interface OnTotalChangedListener{
public void onTotalChanged(float total, List<Debts> selectedDebts);
}
public void setOnTotalChangedListener(OnTotalChangedListener listener) {
this.listener = listener;
}
private class ViewHolder {
TextView concept, descriptionOrDate, amount, expirationDate;
CheckBox checkBox;
}
}
And here is my fragment code:
public class CheckoutFragment extends BaseFragment implements View.OnClickListener, ServicesFinancialStatusAdapter.OnTotalChangedListener {
public static final String TAG = CheckoutFragment.class.getSimpleName();
private List<Debts> debtsList;
private List<Debts> selectedDebts;
private ServicesFinancialStatusAdapter adapter;
private TextView totalView, positiveBalanceView;
private View noDebtsView, header, footer;
private LinearLayout mainLayout;
private float total, positiveBalance;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
debtsList = new ArrayList<Debts>();
adapter = new ServicesFinancialStatusAdapter(getActivity(), debtsList, 5);
adapter.setOnTotalChangedListener(this);
webServices = ((MainActivity) getActivity()).getNetworkInstance();
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Initialize the views
...
reloadData();
onTotalChanged(0, null);
return view;
}
#Override
public void onClick(View view) {
reloadData();
}
#Override
public void onTotalChanged(float total, List<Debts> selectedDebts) {
this.total = total == 0 ? total : total - positiveBalance;
this.selectedDebts = selectedDebts;
totalView.setText(getString(R.string.total_indicator) + Utilities.formatNumberAsMoney(this.total));
getActivity().invalidateOptionsMenu();
}
private void reloadData() {
debtsList.clear();
adapter = new ServicesFinancialStatusAdapter(getActivity(), debtsList, 5);
adapter.setOnTotalChangedListener(this);
loadData();
}
private void loadData() {
//Load debts from server
...
}
private void saveSelectedDebts() {
for (Debts selectedDebt : selectedDebts) {
long id = Debts.insert(getActivity(), selectedDebt);
//Log.d(TAG, "Inserted " + selectedDebt.getConcept() + " with ID " + id);
}
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
if (!((MainActivity) getActivity()).drawerIsOpen) {
inflater.inflate(R.menu.checkout, menu);
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.clear:
adapter.unMarkDebts();
break;
case R.id.checkout:
selectPaymentMode();
break;
}
return super.onOptionsItemSelected(item);
}
private void selectPaymentMode() {
...
}
}
The chat broke. I think one investigation could be:
Everytime you call reload, you are initiating from scracth the adapter with a new object, (call method new). Remove that line and just use the previous adapter (taking care of initiating the adapteer on onCreate). Notice that the vairable selectedDebts is local to the adapter and you are not passing it in the constructor.