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();
}
}
Related
I'm new in this android world. I'm currently working on android project i.e., music player but i got stopped at one point since my recycler view doesn't display anything which is supposed to be a list of songs.
I even checked my logcat but cannot figured out whether the data is binding or not.Any kind of help will be grateful.
SongListAdapter.java
public class SongListAdapter extends RecyclerView.Adapter<SongListAdapter.MyViewHolder> {
ArrayList<SongDetailsJDO> mSongDetailsJDOs;
LayoutInflater mLayoutInflater;
Context mContext;
private static final String TAG = "SongListAdapter";
private boolean mIsSongPlaying = false;
private String mCurrentSongId = "-1";
public SongListAdapter(Context context, ArrayList<SongDetailsJDO> pSongDetailsJDOs) {
mContext = context;
mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mSongDetailsJDOs = pSongDetailsJDOs;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View lView = mLayoutInflater.inflate(R.layout.recycler_view_item, parent, false);
return new MyViewHolder(lView);
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
Uri lUri = null;
if (mSongDetailsJDOs.get(position).getAlbumId() != null && !mSongDetailsJDOs.get(position).getAlbumId().equals("")) {
lUri = ContentUris.withAppendedId(Uri.parse("content://media/external/audio/albumart"), Long.parseLong(mSongDetailsJDOs.get(position).getAlbumId()));
Picasso.with(mContext).load(lUri).resize(100, 100).placeholder(R.drawable.placeholder).into(holder.albumImageIV);
} else
holder.albumImageIV.setImageResource(R.drawable.placeholder);
String lTrackName = mSongDetailsJDOs.get(position).getTitle();
if (lTrackName != null)
holder.trackNameTV.setText(lTrackName.trim());
else
holder.trackNameTV.setText("<Unknown>");
String lAlbumName = mSongDetailsJDOs.get(position).getAlbumName();
if (lAlbumName != null)
holder.albumAndArtistDetailsTV.setText(lAlbumName.trim());
else
holder.albumAndArtistDetailsTV.setText("<Unknown>");
if (mSongDetailsJDOs.get(position).getFavouriteStatus() == 1)
holder.favouriteIV.setImageResource(R.drawable.fav);
else
holder.favouriteIV.setImageResource(R.drawable.fav_u);
// TODO: #holder.animationDrawable use it change Visibility and start (Animation)
if (mIsSongPlaying && mSongDetailsJDOs.get(position).getSongId().equals(mCurrentSongId)) {
holder.eqIv.setVisibility(View.VISIBLE);
holder.animationDrawable = (AnimationDrawable) holder.eqIv.getBackground();
holder.animationDrawable.start();
} else {
holder.eqIv.setVisibility(View.INVISIBLE);
}
}
#Override
public int getItemCount() {
if (mSongDetailsJDOs != null)
return mSongDetailsJDOs.size();
else
return 0;
}
/**
* Called when data is being updated in DB
*/
public void favChanged(int pPosition, int pFavStatus) {
mSongDetailsJDOs.get(pPosition).setFavouriteStatus(pFavStatus);
notifyItemChanged(pPosition);
}
/**
* View Holder class for Rec view
*/
class MyViewHolder extends RecyclerView.ViewHolder {
ImageView albumImageIV;
TextView trackNameTV;
TextView albumAndArtistDetailsTV;
ImageView favouriteIV;
ImageView eqIv;
AnimationDrawable animationDrawable;
MyViewHolder(View itemView) {
super(itemView);
albumImageIV = (ImageView) itemView.findViewById(R.id.album_artwork_iv);
trackNameTV = (TextView) itemView.findViewById(R.id.title_name_tv);
albumAndArtistDetailsTV = (TextView) itemView.findViewById(R.id.artist_author_name_tv);
favouriteIV = (ImageView) itemView.findViewById(R.id.fav_iv);
eqIv = (ImageView) itemView.findViewById(R.id.eq_iv);
}
}
/**
* Swap the data with the new JDO list
*
* #param pSongDetailsJDOs
*/
public void swapData(ArrayList<SongDetailsJDO> pSongDetailsJDOs) {
mSongDetailsJDOs = pSongDetailsJDOs;
notifyDataSetChanged();
}
/**
* Returns the list of currently loaded JDO's
* #return
*/
public List<SongDetailsJDO> getData() {
return mSongDetailsJDOs;
}
/**
* Gets the #{#link SongDetailsJDO} object at the specified position
* #param pPosition
* #return the {#link SongDetailsJDO} object
*/
public SongDetailsJDO getItemAtPosition(int pPosition) {
return mSongDetailsJDOs.get(pPosition);
}
/**
* Update Song Play status
* #param pStatus the status weather is playing or not
* #param lSongId the song id the playing song
*/
public void updateSongPlayStatus(boolean pStatus, String lSongId) {
mIsSongPlaying = pStatus;
mCurrentSongId = lSongId;
notifyDataSetChanged();
}
}
SongListActivity.java
public class SongsListActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor>, SharedPreferences.OnSharedPreferenceChangeListener {
private RecyclerView mRecyclerView;
private SongListAdapter mAdapter;
private ArrayList<SongDetailsJDO> mSongDetailsJDOs;
private TextView mNoSongTV;
private static final int LOADER_ID = 101;
private int REQUEST_CODE = 102;
private static final String TAG = "SongsListActivity";
private SharedPreferences mSharedPreferences;
private SharedPreferences.Editor mPrefEditor;
private SharedPreferences.OnSharedPreferenceChangeListener mOnSharedPreferenceChangeListener;
private FirebaseAnalytics mFirebaseAnalytics;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_main);
mRecyclerView = (RecyclerView) findViewById(R.id.rec_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(SongsListActivity.this));
mNoSongTV = (TextView) findViewById(R.id.no_song_tv);
mSongDetailsJDOs = new ArrayList<>();
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
mPrefEditor = mSharedPreferences.edit();
mSharedPreferences.registerOnSharedPreferenceChangeListener(this);
FirebaseApp.initializeApp(this);
mFirebaseAnalytics = FirebaseAnalytics.getInstance(this);
loadData();
}
private void loadData() {
FirebaseCrash.report(new Exception("OMG An Exception"));
boolean lIsAppLoadingFirstTime = mSharedPreferences.getBoolean(getString(R.string.is_app_loading_first_time), true);
if (lIsAppLoadingFirstTime) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 0);
} else {
mPrefEditor.putBoolean(getString(R.string.is_app_loading_first_time), false);
mPrefEditor.apply();
new LoadDataToDbBackground().execute();
// TODO: Create Loader here
}
} else {
loadDataToRecyclerView();
if (mSharedPreferences.getBoolean(getString(R.string.is_song_playing), false)) {
// TODO: Create Loader here
SongDetailsJDO lJDO = getSongJDO(mSharedPreferences.getString(getString(R.string.song_id), ""));
startActivityForResult(new Intent(SongsListActivity.this, PlayerActivity.class)
.putExtra(getString(R.string.song_jdo), lJDO), REQUEST_CODE);
}
}
}
private class LoadDataToDbBackground extends AsyncTask<Void, Integer, Void> {
ProgressDialog mProgressDialog;
#Override
protected void onPreExecute() {
mProgressDialog = new ProgressDialog(SongsListActivity.this);
mProgressDialog.setMessage("Please Wait");
mProgressDialog.setTitle("Loading");
mProgressDialog.show();
super.onPreExecute();
}
#Override
protected void onPostExecute(Void aVoid) {
mProgressDialog.dismiss();
super.onPostExecute(aVoid);
}
#Override
protected Void doInBackground(Void... params) {
CommonHelper lHelper = new CommonHelper();
lHelper.loadSongToDB(SongsListActivity.this);
runOnUiThread(new Runnable() {
#Override
public void run() {
loadDataToRecyclerView();
}
});
return null;
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
if (requestCode == 0) {
boolean lGranted = true;
for (int lResult : grantResults) {
if (lResult == PackageManager.PERMISSION_DENIED)
lGranted = false;
}
if (lGranted)
loadData();
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE) {
if (data != null && data.getExtras() != null && resultCode == PlayerActivity.RESULT_CODE) {
//if data changed reload the recyclerView
if (data.getBooleanExtra(getString(R.string.is_data_changed), false)) {
mSongDetailsJDOs = new SongDetailTable(this).getAllSongs();
mAdapter.swapData(mSongDetailsJDOs);
}
}
}
// updateCurrentSongIndication();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater lMenuInflater = getMenuInflater();
lMenuInflater.inflate(R.menu.menu_song_list, menu);
SearchManager lSearchManager = (SearchManager) getSystemService(SEARCH_SERVICE);
SearchView lSearchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
lSearchView.setSearchableInfo(lSearchManager.getSearchableInfo(getComponentName()));
lSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
filterRecView(newText);
return true;
}
});
return true;
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(this, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, MediaStore.Audio.Media.TITLE + " ASC");
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
ArrayList<SongDetailsJDO> lSongDetailsNew = new ArrayList<>();
if (data.moveToFirst()) {
do {
lSongDetailsNew.add(new SongDetailsJDO(data.getString(data.getColumnIndex(MediaStore.Audio.Media.TITLE)),
data.getString(data.getColumnIndex(MediaStore.Audio.Media.ALBUM)),
data.getString(data.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID)),
data.getString(data.getColumnIndex(MediaStore.Audio.Media._ID)),
data.getInt(data.getColumnIndex(MediaStore.Audio.Media.DURATION)), 0));
} while (data.moveToNext());
}
compareDataAndMakeChangesToDB(lSongDetailsNew);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
}
#Override
protected void onResume() {
super.onResume();
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
Log.d(TAG, "onWindowFocusChanged: ");
updateCurrentSongIndication();
}
private void updateCurrentSongIndication() {
if (mSharedPreferences.getBoolean(getString(R.string.is_song_playing), false)) {
mAdapter.updateSongPlayStatus(true, mSharedPreferences.getString(getString(R.string.song_id), ""));
mRecyclerView.smoothScrollToPosition(getPositionOfSongId(mSharedPreferences.getString(getString(R.string.song_id), "")));
} else {
if(mAdapter!=null)
mAdapter.updateSongPlayStatus(false, "-1");
}
}
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
updateCurrentSongIndication();
}
private void compareDataAndMakeChangesToDB(ArrayList<SongDetailsJDO> pSongDetailsNew) {
Log.d(TAG, "compareDataAndMakeChangesToDB: Called ============");
ArrayList<String> lSongIdsToBeDeleted = new ArrayList<>();
for (SongDetailsJDO lSongDetailsJDO : mSongDetailsJDOs) {
lSongIdsToBeDeleted.add(lSongDetailsJDO.getSongId());
}
ArrayList<SongDetailsJDO> lNewSongsToBeAdded = new ArrayList<>();
for (SongDetailsJDO lSongDetailsJDO : pSongDetailsNew) {
if (lSongIdsToBeDeleted.contains(lSongDetailsJDO.getSongId())) {
lSongIdsToBeDeleted.remove(lSongDetailsJDO.getSongId());
} else
lNewSongsToBeAdded.add(lSongDetailsJDO);
}
if (lSongIdsToBeDeleted.size() > 0 || lNewSongsToBeAdded.size() > 0) {
SongDetailTable lSongDetailTable = new SongDetailTable(this);
lSongDetailTable.removeSongsForIds(lSongIdsToBeDeleted);
lSongDetailTable.insertSongs(lNewSongsToBeAdded);
loadDataToRecyclerView();
//
// SongPlayerService lSongPlayerService = SongPlayerService.getRunningInstance();
// if (lSongPlayerService != null)
// lSongPlayerService.dataChanged();
}
}
public void onFavClick(View pView) {
//Firebase Logging
Bundle lBundle = new Bundle();
lBundle.putString(FirebaseAnalytics.Param.ITEM_CATEGORY,"Favourite Clicked");
mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.VIEW_ITEM,lBundle);
int lPosition = mRecyclerView.getChildLayoutPosition((View) pView.getParent());
SongDetailsJDO lSongDetailsJDO = mAdapter.getItemAtPosition(lPosition);
String lSongId = lSongDetailsJDO.getSongId();
SongDetailTable lSongDetailTable = new SongDetailTable(this);
int lNewFavStatus = lSongDetailsJDO.getFavouriteStatus() == 0 ? 1 : 0;
lSongDetailTable.setFavouriteStatus(lSongId, lNewFavStatus);
mAdapter.favChanged(lPosition, lNewFavStatus);
SongPlayerService mSongPlayerService = SongPlayerService.getRunningInstance();
if (mSongPlayerService != null)
mSongPlayerService.favChanged(lPosition, lNewFavStatus);
}
public void onRowClick(View pView) {
int lPosition = mRecyclerView.getChildLayoutPosition(pView);
SongDetailsJDO lJDO = mAdapter.getItemAtPosition(lPosition);
startActivityForResult(new Intent(SongsListActivity.this, PlayerActivity.class)
.putExtra(getString(R.string.song_jdo), lJDO), REQUEST_CODE);
overridePendingTransition(R.anim.from_right, R.anim.scale_down);
}
private SongDetailsJDO getSongJDO(String pSongId) {
SongDetailsJDO lJDO = null;
for (SongDetailsJDO lSongDetailsJDO : mSongDetailsJDOs) {
if (lSongDetailsJDO.getSongId().equals(pSongId)) {
lJDO = lSongDetailsJDO;
break;
}
}
return lJDO;
}
private void filterRecView(String pText) {
if (pText != null) {
if (pText.equals("")) {
mAdapter.swapData(mSongDetailsJDOs);
toggleVisibilityForNoResult(mSongDetailsJDOs.size(), pText);
} else {
ArrayList<SongDetailsJDO> lSongDetailsJDOs = new ArrayList<>();
pText = pText.toLowerCase();
for (SongDetailsJDO lDetailsJDO : mSongDetailsJDOs) {
if (lDetailsJDO.getTitle().toLowerCase().contains(pText) || lDetailsJDO.getAlbumName() != null && lDetailsJDO.getAlbumName().toLowerCase().contains(pText))
lSongDetailsJDOs.add(lDetailsJDO);
}
toggleVisibilityForNoResult(lSongDetailsJDOs.size(), pText);
mAdapter.swapData(lSongDetailsJDOs);
}
}
}
public void toggleVisibilityForNoResult(int pNumberOfSongs, String query) {
if (pNumberOfSongs == 0) {
mNoSongTV.setVisibility(View.VISIBLE);
mNoSongTV.setText(getString(R.string.nosong) + " " + query);
} else
mNoSongTV.setVisibility(View.INVISIBLE);
}
public void loadDataToRecyclerView() {
//Loading data to RecyclerView
mSongDetailsJDOs = new SongDetailTable(this).getAllSongs();
mAdapter = new SongListAdapter(SongsListActivity.this, mSongDetailsJDOs);
mRecyclerView.setAdapter(mAdapter);
}
public int getPositionOfSongId(String pSongId) {
int lPostion = -1;
for (int i = 0; i < mSongDetailsJDOs.size(); i++) {
if (mSongDetailsJDOs.get(i).getSongId().equals(pSongId)) {
lPostion = i;
break;
}
}
return lPostion;
}
}
Looking at your problem its not possible to tell exact cause of an issue.
But still i will give some hint over issue.
Check your code inside
mSongDetailsJDOs.size(); what is size of the list, it should be > 0. If not then check inside your activity how you are passing list.
#Override
public int getItemCount() {
if (mSongDetailsJDOs != null)
return mSongDetailsJDOs.size();
else
return 0;
}
If above list is > 0 then check it inside onBindViewHolder() that you are getting position one by one and try to render one item a time.
let me know if above works for you.
UPDATE
Thanks to #EmanuelSeibold I was able to pinpoint the problem to the update of the recyclerview. The AsyncTask works in the background just fine, and only the adapter update of the recyclerview freezes the UI.
UPDATE2
I found it was indeed my layout setup. I forgot to remove the nestedScrollView around the RecyclerView. That seemed to cause a rendering conflict.
I dug my way through answers here and blog posts, but just don't seem to be able to find a solution.
I am fairly new to Android development and trying to get an idea on multi-threading.
The scenario: I have an app that holds a SQLite database with course data and implemented a search function that queries that database. This blocks the UI-thread for roughly ~3 seconds.
I therefore implemented an AsyncTask to keep the UI responsive, but my UI is still blocked while the search is ongoing.
Thanks in advance!
Here the code:
Search activity
public class Activity_Search extends Activity_Base {
private RecyclerView rv;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
setActionBarTitle(R.string.title_search);
findViewById(R.id.searchCourseTitle).setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
switch(keyCode) {
case KeyEvent.KEYCODE_ENTER:
startSearch();
break;
default:
return false;
}
return true;
}
});
rv = (RecyclerView) findViewById(R.id.searchRecycler);
Adapter_Search adapter = new Adapter_Search(this, null);
rv.setAdapter(adapter);
rv.setLayoutManager(new LinearLayoutManager(this));
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.search_FAB);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
startSearch();
}
});
}
private void startSearch() {
findViewById(R.id.searchCourseTitle).clearFocus();
if (this.getCurrentFocus() != null) {
InputMethodManager imm =
(InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(), 0);
}
{getting input}
String[] columns = {...};
String selection = {formatting input};
Async_Search search = new Async_Search(this);
search.execute(columns, new String[]{selection});
}
public void onSearchCompleted(Cursor results) {
((Adapter_Search) rv.getAdapter()).changeCursor(results);
}
}
AsyncTask
public class Async_Search extends AsyncTask<String[], Void, Cursor> {
private Activity activity;
public Async_Search (Activity activity) {
this.activity = activity;
}
#Override
protected Cursor doInBackground(String[]... params) {
SQLiteDatabase db = SQL_Database.getInstance(activity).getWritableDatabase();
String[] columns = params[0];
String selection = params[1][0];
return db.query(...)
}
#Override
protected void onPostExecute(Cursor results) {
((Activity_Search) activity).onSearchCompleted(results);
}
}
Recycler adapter
public class Adapter_Search extends RecyclerCursorAdapter<Adapter_Search.ViewHolder>{
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView name, ects, studipCode, termYear, lecturers, fields;
public ViewHolder (View view) {
super(view);
{id lookups}
}
}
public Adapter_Search(Context context, Cursor cursor) {
super(context, cursor);
}
#Override
public ViewHolder onCreateViewHolder (ViewGroup parent, int ViewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.search_list_entry, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder (ViewHolder viewHolder, Cursor cursor) {
TextView name, ects, studipCode, termYear, lecturers, fields;
name = viewHolder.name;
ects = viewHolder.ects;
studipCode = viewHolder.studipCode;
termYear = viewHolder.termYear;
lecturers = viewHolder.lecturers;
fields = viewHolder.fields;
String termandyear = cursor.getString(cursor.getColumnIndexOrThrow(SQL_Database.COURSE_COLUMN_TERM)) +
String.format("%.2s",cursor.getString(cursor.getColumnIndexOrThrow(SQL_Database.COURSE_COLUMN_YEAR)));
name.setText(cursor.getString(cursor.getColumnIndexOrThrow(SQL_Database.COURSE_COLUMN_COURSE)));
String credits = cursor.getString(cursor.getColumnIndexOrThrow(SQL_Database.COURSE_COLUMN_ECTS)) + " ECTS";
ects.setText(credits);
{and so on}
}
}
Base adapter class
public abstract class RecyclerCursorAdapter <ViewHolder extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<ViewHolder> {
private Context mContext;
private Cursor mCursor;
private boolean mDataValid;
private int mRowIdColumn;
private DataSetObserver mDataSetObserver;
public RecyclerCursorAdapter(Context context, Cursor cursor) {
mContext = context;
mCursor = cursor;
mDataValid = cursor != null;
mRowIdColumn = mDataValid ? mCursor.getColumnIndex("_id") : -1;
mDataSetObserver = new NotifyingDataSetObserver();
if (mCursor != null) {
mCursor.registerDataSetObserver(mDataSetObserver);
}
}
public Cursor getCursor() {
return mCursor;
}
#Override
public int getItemCount() {
if (mDataValid && mCursor != null) {
return mCursor.getCount();
}
return 0;
}
#Override
public long getItemId(int position) {
if (mDataValid && mCursor != null && mCursor.moveToPosition(position)) {
return mCursor.getLong(mRowIdColumn);
}
return 0;
}
#Override
public void setHasStableIds(boolean hasStableIds) {
super.setHasStableIds(true);
}
public abstract void onBindViewHolder(ViewHolder viewHolder, Cursor cursor);
#Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
if (!mDataValid) {
throw new IllegalStateException("this should only be called when the cursor is valid");
}
if (!mCursor.moveToPosition(position)) {
throw new IllegalStateException("couldn't move cursor to position " + position);
}
onBindViewHolder(viewHolder, mCursor);
}
/**
* Change the underlying cursor to a new cursor. If there is an existing cursor it will be
* closed.
*/
public void changeCursor(Cursor cursor) {
Cursor old = swapCursor(cursor);
if (old != null) {
old.close();
}
}
/**
* Swap in a new Cursor, returning the old Cursor. Unlike
* {#link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
* closed.
*/
public Cursor swapCursor(Cursor newCursor) {
if (newCursor == mCursor) {
return null;
}
final Cursor oldCursor = mCursor;
if (oldCursor != null && mDataSetObserver != null) {
oldCursor.unregisterDataSetObserver(mDataSetObserver);
}
mCursor = newCursor;
if (mCursor != null) {
if (mDataSetObserver != null) {
mCursor.registerDataSetObserver(mDataSetObserver);
}
mRowIdColumn = newCursor.getColumnIndexOrThrow("_id");
mDataValid = true;
notifyDataSetChanged();
} else {
mRowIdColumn = -1;
mDataValid = false;
notifyDataSetChanged();
//There is no notifyDataSetInvalidated() method in RecyclerView.Adapter
}
return oldCursor;
}
private class NotifyingDataSetObserver extends DataSetObserver {
#Override
public void onChanged() {
super.onChanged();
mDataValid = true;
notifyDataSetChanged();
}
#Override
public void onInvalidated() {
super.onInvalidated();
mDataValid = false;
notifyDataSetChanged();
//There is no notifyDataSetInvalidated() method in RecyclerView.Adapter
}
}
}
Maybe try to remove these lines from the Async Search.
private Activity activity;
public Async_Search (Activity activity) {
this.activity = activity;
}
Instead call like this:
Async_Search search = new Async_Search();
search.execute(columns, new String[]{selection});
I don't know if this will work, but my Asynctask doesn't use the activity stuff yours has.
I have a recycler view in which I display phone contacts. You can select or deselect a contact in the list thanks to a checkbox. The recycler view is displayed on an activity. Navigating to that activity, I transfer all contacts that have been selected before and when I'm displaying the list, I compare the phone contacts to the selected ones in order to select them by default in the list. So I have two lists passed to the adapter: phone contacts list and selected contacts list. I let the user filter the contacts list by display name so I bind a TextWatcher event on the EditText, which reload the recyclerview after the text has changed. But the problem is when the user starts to filter, the selected contacts list is being populated from nowhere and I got all the phone contacts in the selected contacts list.... Very strange...
Here is the adapter:
public class ContactsChooserAdapter extends RecyclerView.Adapter<ContactChooserViewHolder> {
private ArrayList<Contact> mContacts;
private ArrayList<Contact> mSelectedContacts;
private OnContactSelectionListener mContactListener;
public ContactsChooserAdapter(ArrayList<Contact> contacts, ArrayList<Contact> selectedContacts)
{
mContacts = contacts;
mSelectedContacts = selectedContacts;
}
public void setOnContactSelectionListener(OnContactSelectionListener listener)
{
mContactListener = listener;
}
#Override
public ContactChooserViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.contacts_chooser_list_item, parent, false);
ContactChooserViewHolder holder = new ContactChooserViewHolder(v);
return holder;
}
#Override
public void onBindViewHolder(ContactChooserViewHolder holder, int position) {
Contact contact = getContacts().get(position);
if(isAlreadySelected(contact)){
holder.mChoosed.setChecked(true);
}else{
holder.mChoosed.setChecked(false);
}
holder.mContactDisplayName.setText(contact.getDisplayName());
holder.mContactPhoneNumber.setText(contact.getPhoneNumbers().get(0));
if(mContactListener != null) {
holder.bind(contact, mContactListener);
}
}
#Override
public int getItemCount() {
return getContacts().size();
}
/**
* Method which allows to check whenever a contact is already selected in a list or not.
* #param contact
* #return
*/
private boolean isAlreadySelected(Contact contact){
boolean alreadySelected = false;
if(getAlreadySelectedContacts() != null){
System.out.println("ALready selected contact list: "+ getAlreadySelectedContacts().size());
for(Contact c : getAlreadySelectedContacts()){
if(c.getId() == contact.getId()){
alreadySelected = true;
break;
}
}
}
return alreadySelected;
}
public ArrayList<Contact> getContacts() {
return mContacts;
}
public void setContacts(ArrayList<Contact> mContacts) {
this.mContacts = mContacts;
}
public ArrayList<Contact> getAlreadySelectedContacts() {
return mSelectedContacts;
}
public void setAlreadySelectedContacts(ArrayList<Contact> selectedContacts) {
this.mSelectedContacts = selectedContacts;
}
}
Here is the activity:
public class ContactsActivity extends AppCompatActivity {
private ArrayList<Contact> mSelectedContacts;
private ArrayList<Contact> mPhoneContacts;
private RecyclerView mContactsList;
private BatifyTextViewBold mContactsNbLabel;
private BatifyEditText mSearchEditText;
private ContactsChooserAdapter mContactsChooserAdapter;
private LinearLayoutManager mLinearLayoutManager;
private RelativeLayout mCancelBtn;
private RelativeLayout mFinishBtn;
private ImageView mSearchIcon;
private RelativeLayout mListHeader;
private RelativeLayout mSearchLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_contacts);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = this.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.setStatusBarColor(ContextCompat.getColor(this, R.color.batifyGreen));
}
Bundle bundle = getIntent().getExtras();
ArrayList<Contact> transmittedContacts = bundle.getParcelableArrayList(GlobalVariables.CONTACTS_SELECTED_CONTACTS_LABEL);
mSelectedContacts = new ArrayList<>();
if(transmittedContacts != null) {
mSelectedContacts.addAll(transmittedContacts);
}
mContactsList = (RecyclerView)findViewById(R.id.contactsList);
mContactsNbLabel = (BatifyTextViewBold)findViewById(R.id.contactNbSelectedTv);
if(getSelectedContacts() == null){
mContactsNbLabel.setText("0 contact ");
}else{
mContactsNbLabel.setText(getSelectedContacts().size() + (getSelectedContacts().size() > 1 ? " contacts " : " contact "));
}
mFinishBtn = (RelativeLayout)findViewById(R.id.finishContactsBtn);
mFinishBtn.setOnClickListener(finishChoosingContactsListener);
mCancelBtn = (RelativeLayout)findViewById(R.id.cancelContactsBtn);
mCancelBtn.setOnClickListener(cancelChoosingContactsListener);
mSearchIcon = (ImageView)findViewById(R.id.searchBtn);
mSearchIcon.setOnClickListener(searchListener);
mListHeader = (RelativeLayout)findViewById(R.id.listHeader);
mListHeader.setVisibility(View.VISIBLE);
mSearchLayout = (RelativeLayout)findViewById(R.id.searchLayout);
mSearchLayout.setVisibility(View.GONE);
mSearchEditText = (BatifyEditText)findViewById(R.id.searchContactsEditText);
mSearchEditText.addTextChangedListener(searchViewWatcher);
requestPermissionForContactsAndProcess();
}
/**
* Method that requests the permission to access contacts.
*/
public void requestPermissionForContactsAndProcess() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED)
{
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_CONTACTS)) {
Tools.showPopUpCallBack(this, "Information", getString(R.string.contactRationalMessage), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_POSITIVE)
{
requestPermissionForContactsAndProcess();
}
}
});
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS}, GlobalVariables.CONTACTS_PERMISSIONS);
}
}else{
getAllContactsAndDisplay();
}
}
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case GlobalVariables.CONTACTS_PERMISSIONS: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
getAllContactsAndDisplay();
}
return;
}
}
}
/**
* Method which allows to display a contacts list
*/
public void displayContactList(){
mContactsChooserAdapter = new ContactsChooserAdapter(getPhoneContacts(), getSelectedContacts());
mContactsChooserAdapter.setOnContactSelectionListener(contactSelectionListener);
mLinearLayoutManager = new LinearLayoutManager(this);
mContactsList.setLayoutManager(mLinearLayoutManager);
mContactsList.setAdapter(mContactsChooserAdapter);
}
/**
* Method that will retrieve all contacts.
*/
private void getAllContactsAndDisplay()
{
mPhoneContacts = new ArrayList<>();
Contact contact;
ContentResolver contentResolver = getContentResolver();
Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC");
if (cursor.getCount() > 0) {
while (cursor.moveToNext()) {
int hasPhoneNumber = Integer.parseInt(cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)));
if (hasPhoneNumber > 0) {
String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
contact = new Contact();
contact.setId(Integer.parseInt(id));
contact.setDisplayName(displayName);
Cursor phoneCursor = contentResolver.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
new String[]{id},
null);
if (phoneCursor.moveToNext()) {
String phoneNumber = phoneCursor.getString(phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
contact.getPhoneNumbers().add(phoneNumber);
}
phoneCursor.close();
getPhoneContacts().add(contact);
}
}
}
displayContactList();
}
/**
* Method which allow to finish and close the activity. It returns a result.
*/
public void finishedChoosingContacts()
{
Intent intent= new Intent();
intent.putParcelableArrayListExtra(GlobalVariables.CONTACTS_SELECTED_CONTACTS_LABEL, getSelectedContacts());
setResult(Activity.RESULT_OK, intent);
finish();
}
/**
* Listener which allows to select and deselect a contact in a list.
*/
private OnContactSelectionListener contactSelectionListener = new OnContactSelectionListener() {
#Override
public void onContactSelected(Contact contact) {
if(getSelectedContacts() == null){
setSelectedContacts(new ArrayList<Contact>());
}
getSelectedContacts().add(contact);
mContactsNbLabel.setText(getSelectedContacts().size() + (getSelectedContacts().size() > 1 ? " contacts " : " contact "));
}
#Override
public void onContactDeSelected(Contact contact) {
int indexToDelete = -1;
for(Contact c : getSelectedContacts()){
if(c.getId() == contact.getId()){
indexToDelete = getSelectedContacts().indexOf(c);
break;
}
}
if(indexToDelete > -1) {
getSelectedContacts().remove(indexToDelete);
mContactsNbLabel.setText(getSelectedContacts().size() + (getSelectedContacts().size() > 1 ? " contacts " : " contact "));
}
}
};
/**
* Listener which is fired when the search edit text value is changing.
*/
private TextWatcher searchViewWatcher = new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
filterListAndUpdate(s.toString());
}
};
/**
* method used to filter and update the list
* #param searchedValue
*/
private void filterListAndUpdate(String searchedValue)
{
ArrayList<Contact> associatedSearch = new ArrayList<>();
if(!"".equals(searchedValue) && searchedValue != null) {
for (Contact c : getPhoneContacts()) {
if (c.getDisplayName().toUpperCase().startsWith(searchedValue.toUpperCase())) {
associatedSearch.add(c);
}
}
((ContactsChooserAdapter)mContactsList.getAdapter()).setContacts(associatedSearch);
}else{
((ContactsChooserAdapter)mContactsList.getAdapter()).setContacts(getPhoneContacts());
}
mContactsList.getAdapter().notifyDataSetChanged();
}
/**
* Listener that allows to fire the search listener.
*/
private View.OnClickListener searchListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
mListHeader.setVisibility(View.GONE);
mSearchLayout.setVisibility(View.VISIBLE);
}
};
/**
* Listener which is fired when the user cancels the contacts selection.
*/
private View.OnClickListener cancelChoosingContactsListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
finish();
}
};
/**
* Listener which id fired when the user finishes selecting contacts.
*/
private View.OnClickListener finishChoosingContactsListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
finishedChoosingContacts();
}
};
public ArrayList<Contact> getSelectedContacts() {
return mSelectedContacts;
}
public void setSelectedContacts(ArrayList<Contact> mSelectedContacts) {
this.mSelectedContacts = mSelectedContacts;
}
public ArrayList<Contact> getPhoneContacts() {
return mPhoneContacts;
}
}
I am using a recyclerView to show a grid of movie posters. The posters are contained in a List<> along with their respective title and so on.
I implemented a searchView widget and I can successuflly get a List of matching results. But I can't hide the other ones.
As you understand I don't want to delete the irrelevant movies from the adapter or the user would not be able to see them again.
This is the code:
public class SearchUtils {
public static List<String> search(List<Show> list, String keyword){
List<String> results = new ArrayList<>();
for (Show curVal : list){
String curTitle = curVal.getTitle().toLowerCase().trim();
if (curTitle.contains(keyword)){
results.add(curTitle);
}else{
results = new ArrayList<>();
}
}
return results;
}
}
ListFragment.java
public class ListFragment extends Fragment implements LoaderManager.LoaderCallbacks<List<Show>> {
private static final String LOG_TAG = "ListFragment";
private static final String ARG_SCOPE = "com.dcs.shows.activity_to_launch";
private static final String BASE_URL = "http://api.themoviedb.org/3";
private TextView tv;
private ProgressBar pb;
private int scope;
private RecyclerView mRecyclerView;
private ShowAdapter mShowAdapter;
private SearchView mSearchView;
public static ListFragment newInstance(int target) {
Bundle args = new Bundle();
args.putInt(ARG_SCOPE, target);
ListFragment fragment = new ListFragment();
fragment.setArguments(args);
return fragment;
}
public ListFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
scope = getArguments().getInt(ARG_SCOPE);
setHasOptionsMenu(true);
Log.i(LOG_TAG, "onCreate#Scope is: " + scope);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_list, container, false);
mShowAdapter = new ShowAdapter(new ArrayList<Show>());
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerView);
GridLayoutManager glm = new GridLayoutManager(getActivity(), 4);
mRecyclerView.setLayoutManager(glm);
mRecyclerView.addItemDecoration(new SpacesItemDecoration(8, getActivity()));
mRecyclerView.setAdapter(mShowAdapter);
mRecyclerView.addOnScrollListener(new EndlessRecyclerViewScrollListener(glm) {
#Override
public void onLoadMore(int page, int totalItemsCount) {
// Triggered only when new data needs to be appended to the list
// Add whatever code is needed to append new items to the bottom of the list
}
});
pb = (ProgressBar)rootView.findViewById(R.id.progress_view);
ConnectivityManager connMgr = (ConnectivityManager) getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected()) {
// fetch data
getLoaderManager().initLoader(1, null, this);
} else {
// display error
pb.setVisibility(View.GONE);
}
return rootView;
}
List<Show> searchList;
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
getActivity().getMenuInflater().inflate(R.menu.main, menu);
final MenuItem myActionMenuItem = menu.findItem( R.id.action_search);
mSearchView = (SearchView) myActionMenuItem.getActionView();
mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String s) {
if(!mSearchView.isIconified()) {
mSearchView.setIconified(true);
}
myActionMenuItem.collapseActionView();
return false;
}
#Override
public boolean onQueryTextChange(String s) {
if(s != null || !s.isEmpty()) {
for(Show movie : mShowAdapter.getList()) {
if(movie.getTitle().toLowerCase().contains(s.toLowerCase())){
mShowAdapter.add(movie);
}
mShowAdapter.notifyDataSetChanged();
}
} else {
mShowAdapter.addItemsToList(searchList, false);
}
return false;
}
});
mSearchView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
#Override
public void onViewDetachedFromWindow(View arg0) {
// search was detached/closed
Log.v(LOG_TAG, "Restoring list: " + searchList + " size: " + searchList.size());
mShowAdapter.addItemsToList(searchList, false);
}
#Override
public void onViewAttachedToWindow(View arg0) {
// search was opened
searchList = mShowAdapter.getList();
}
});
}
private class ShowHolder extends RecyclerView.ViewHolder {
public ImageView mImageView;
public TextView mTextView;
public ShowHolder(View itemView) {
super(itemView);
mImageView = (ImageView) itemView.findViewById(R.id.grid_item_image);
mTextView = (TextView) itemView.findViewById(R.id.grid_item_title);
}
}
private class ShowAdapter extends RecyclerView.Adapter<ShowHolder> {
private List<Show> mShows;
public ShowAdapter(List<Show> shows) {
mShows = shows;
}
public void add(Show show){
mShows.add(show);
notifyDataSetChanged();
}
public void addItemsToList(List<Show> newShows, boolean append){
if(append){
mShows.addAll(newShows);
}else {
mShows = newShows;
}
notifyDataSetChanged();
}
public void removeItemsFromList(int index){
mShows.remove(index);
notifyItemRemoved(index);
}
public List<Show> getList(){
return mShows;
}
#Override
public ShowHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(getActivity());
View rootView = inflater.inflate(R.layout.list_item_row, parent, false);
return new ShowHolder(rootView);
}
#Override
public void onBindViewHolder(ShowHolder holder, int position) {
Show currentShow = mShows.get(position);
holder.mTextView.setText(currentShow.getTitle());
Glide.with(getActivity()).load(currentShow.getImage()).into(holder.mImageView);
}
#Override
public int getItemCount() {
return mShows.size();
}
}
#Override
public Loader<List<Show>> onCreateLoader(int i, Bundle bundle) {
//start the loader with the appropriate uri
//for now it only supports movies+popular
//it will support movies+top, tv+popular, tv+top.
Uri baseUri = Uri.parse(BASE_URL);
Uri.Builder uriBuilder = baseUri.buildUpon();
uriBuilder.appendPath("movie");
uriBuilder.appendPath("popular");
uriBuilder.appendQueryParameter("api_key", QueryUtils.API_KEY);
uriBuilder.appendQueryParameter("page", Integer.valueOf(1).toString());
Log.v(LOG_TAG, "onCreateLoader#URL built: " + uriBuilder.toString());
return new ShowLoader(getActivity(), uriBuilder.toString());
}
#Override
public void onLoadFinished(Loader<List<Show>> loader, List<Show> shows) {
// Clear the adapter of previous earthquake data
clearAdapter();
// If there is a valid list of Shows, then add them to the adapter's
// data set. This will trigger the ListView to update.
if (shows != null && !shows.isEmpty()) {
mShowAdapter.addItemsToList(shows, false);
mShowAdapter.notifyDataSetChanged();
}
pb.setVisibility(View.GONE);
}
#Override
public void onLoaderReset(Loader<List<Show>> loader) {
// Loader reset, so we can clear out our existing data.
clearAdapter();
}
private void clearAdapter(){
List<Show> empty = new ArrayList<>();
mShowAdapter.addItemsToList(empty, false);
mShowAdapter.notifyDataSetChanged();
}
Thanks
You can use two lists, one with all the elements (original), and one with just queried elements (this one should use recyclerview adapter). When querying, just select from original list and add them to adapter list, then notify changes. Don't forget to clear adapter list before adding new entries.
Edit: you can try something like this on onQueryTextChange method. Adapt for your own wish.
if(s != null && !s.isEmpty()) {
for(String movie : originalList) {
if(movie.toLowerCase().contains(s.toLowerCase()){
adapter.add(movie);
}
notifyChanges();
}
}
} else { adapter.addAll(originalList); }
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?