The goal of the code is to query the google api for a list of books that the user has chosen. The app takes the text inputted in the editText field, and when the user clicks the search button, the query parameter in the query url should be changed to the inputted text then another network request is made using a loader. I can't seem to get this to function. Below is the code:
Query URLS
private static String queryUrl = "https://www.googleapis.com/books/v1/volumes?q=android&maxResults=20&orderBy=relevance";
private static String baseUrl = "https://www.googleapis.com/books/v1/volumes?";
Search Button On Click
//Method that takes the current URL and modifies it based on what the user searched
private void searchButtonOnClick() {
Log.d(LOG_TAG, "searchButtonOnClick was called");
//Get the user input
if (TextUtils.isEmpty(editText.getText())) {
Toast.makeText(this, "No Search Entered", Toast.LENGTH_SHORT).show();
} else {
Uri.Builder uriBuilder = new Uri.Builder();
uriBuilder.scheme("https")
.authority("www.googleapis.com")
.appendPath("books")
.appendPath("v1")
.appendPath("volumes")
.appendQueryParameter("q", editText.getText().toString())
.appendQueryParameter("maxResults", "100")
.appendQueryParameter("orderBy", "relevance");
queryUrl = uriBuilder.build().toString();
LoaderManager loaderManager = getSupportLoaderManager();
booksListAdapter.clear();
loaderManager.initLoader(BOOK_LOADER_ID, null, this);
}
}
Full MainActivity Class
package com.example.booksearch;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import android.net.Uri.Builder;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.squareup.picasso.Picasso;
import java.lang.reflect.Array;
import java.net.NetworkInterface;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<List<Book>> {
//Log tag for debugging purposes
public static final String LOG_TAG = MainActivity.class.getSimpleName();
//Getting title author and image, link book to where to buy when clicked
private static String input = "";
private static String queryUrl = "https://www.googleapis.com/books/v1/volumes?q=android&maxResults=20&orderBy=relevance";
private static String baseUrl = "https://www.googleapis.com/books/v1/volumes?";
private EditText editText;
private List<Book> books = new ArrayList<>();
private static BooksListAdapter booksListAdapter;
private static int BOOK_LOADER_ID;
private Button searchButton;
private TextView emptyTextView;
private ImageView imageView;
private ProgressBar loadProgressIndicator;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Hook up search button
searchButton = findViewById(R.id.search_button);
//Hook up editText
editText = findViewById(R.id.search_edit_text);
//setting up searchButtonOnclickListener
searchButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
searchButtonOnClick();
}
});
//Get a connectivity Manager to monitor network state
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(this.CONNECTIVITY_SERVICE);
//Get details about the devices network
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
//Hook up the empty textView
emptyTextView = findViewById(R.id.empty_text_view);
//Hook up progress indicator
loadProgressIndicator = findViewById(R.id.loading_data_progress_indicator);
//If there is a network connection fetch data if not, set the text on the empty text view accordingly
if (networkInfo != null && networkInfo.isConnected()) {
LoaderManager loaderManager = getSupportLoaderManager();
loaderManager.initLoader(BOOK_LOADER_ID, null, this);
} else {
//if no internet connection display error message
loadProgressIndicator.setVisibility(View.GONE);
emptyTextView.setText(R.string.no_internet);
}
// the Book objects are added to this array list off the main thread, thats why the adapter is set to an empty array list.
//This line of code will always be executed before the asynchonous Load is done, because it's on the main thread.
booksListAdapter = new BooksListAdapter(this, new ArrayList<Book>());
ListView bookListView = findViewById(R.id.book_list_view);
bookListView.setAdapter(booksListAdapter);
bookListView.setEmptyView(emptyTextView);
//Set onclick listener on the list to send the user to where to buy the book
bookListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Book currentBook = booksListAdapter.getItem(position);
//Check to see if there is a place to buy the book before sending the user off
if (currentBook.getWhereToBuyBook().isEmpty()) {
Toast.makeText(MainActivity.this, "Cannot find where to purchase", Toast.LENGTH_SHORT).show();
} else {
Uri bookURi = Uri.parse(currentBook.getWhereToBuyBook());
//Creating an intent to send the user to a website
Intent websiteIntent = new Intent(Intent.ACTION_VIEW, bookURi);
//send the intent to another app that can handle it
startActivity(websiteIntent);
}
}
});
}
#NonNull
#Override
public Loader<List<Book>> onCreateLoader(int id, #Nullable Bundle args) {
return new BookLoader(this, queryUrl);
}
#Override
public void onLoadFinished(#NonNull Loader<List<Book>> loader, List<Book> data) {
//After the first results load check for internet connectivity to avoid error message loading at app startup
ConnectivityManager cm = (ConnectivityManager) this.getSystemService(CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
if (networkInfo == null) {
emptyTextView.setText(R.string.no_internet);
} else if (networkInfo != null && networkInfo.isConnected()) {
//display when there is internet but there were no results
emptyTextView.setText(R.string.no_books_found);
}
booksListAdapter.clear();
//If there is a valid list of books, add them to the dataset, this will trigger the listview to update
if (data != null && !data.isEmpty()) {
booksListAdapter.addAll(data);
} else {
emptyTextView.setText(R.string.no_books_found);
}
loadProgressIndicator.setVisibility(View.GONE);
}
#Override
public void onLoaderReset(#NonNull Loader<List<Book>> loader) {
booksListAdapter.clear();
}
//Method that takes the current URL and modifies it based on what the user searched
private void searchButtonOnClick() {
Log.d(LOG_TAG, "searchButtonOnClick was called");
//Get the user input
if (TextUtils.isEmpty(editText.getText())) {
Toast.makeText(this, "No Search Entered", Toast.LENGTH_SHORT).show();
} else {
Uri buildUri = Uri.parse(baseUrl);
Uri.Builder uriBuilder = new Uri.Builder();
buildUri.buildUpon();
uriBuilder.scheme("https")
.authority("www.googleapis.com")
.appendPath("books")
.appendPath("v1")
.appendPath("volumes")
.appendQueryParameter("q", editText.getText().toString())
.appendQueryParameter("maxResults", "100")
.appendQueryParameter("orderBy", "relevance");
queryUrl = uriBuilder.build().toString();
booksListAdapter.clear();
LoaderManager loaderManager = getSupportLoaderManager();
loaderManager.restartLoader(BOOK_LOADER_ID, null, this);
loaderManager.initLoader(BOOK_LOADER_ID, null, this).forceLoad();
}
}
}
As per the code snippet you have shared, .appendQueryParameter("q", editText.getText().toString()) is taking the query param already. Can you explain a bit more, what exactly is the issue you are facing?
Update:
Change your query to --
private static String baseUrl = "https://www.googleapis.com/books/v1/volumes?maxResults=20&orderBy=relevance&q=";
Change your else block to this -
else {
queryUrl = baseUrl + editText.getText().toString();
LoaderManager loaderManager = getSupportLoaderManager();
booksListAdapter.clear();
loaderManager.initLoader(BOOK_LOADER_ID, null, this);
}
Related
I have implemented a loader callback with my MainActivity but running into a weird issue with the onCreateLoader() method
package com.example.newsapp;
import androidx.appcompat.app.AppCompatActivity;
import android.app.LoaderManager;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.Loader;
import android.os.AsyncTask;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity implements LoaderCallbacks<List<News>> {
public static final String LOG_TAG = MainActivity.class.getName();
/** TextView that is displayed when the list is empty */
private TextView mEmptyStateTextView;
//Adapter for the list of news objects
private NewsAdapter mAdapter;
//Get the url for the earthquake data set from USGS
private static final String NEWS_REQUEST_URL =
"http://content.guardianapis.com/search?q=debates&api-key=test";
//Constant value for the earthquake loader ID. We can choose any integer.
private static final int NEWS_LOADER_ID = 1;
//Loader callback method: onCreateLoader()
#Override
public Loader<List<News>> onCreateLoader(int id, Bundle bundle) {
Log.i(LOG_TAG, "TEST: OnCreateLoader() called");
//create a new loader for the given url
// return new Loader<List<News>>(this, USGS_REQUEST_URL);
// parse breaks apart the URI string that's passed into its parameter
Uri baseUri = Uri.parse(NEWS_REQUEST_URL);
// buildUpon prepares the baseUri that we just parsed so we can add query parameters to it
Uri.Builder uriBuilder = baseUri.buildUpon();
// Append query parameter and its value. For example, the `format=geojson`
//https://content.guardianapis.com/search?q=debate&tag=politics/politics&from-date=2014-01-01&api-key=test
uriBuilder.appendQueryParameter("q", "debate");
uriBuilder.appendQueryParameter("tag", "politics/politics");
uriBuilder.appendQueryParameter("from-date", "2014-01-01");
uriBuilder.appendQueryParameter("api-key", "test");
//Return the completed uri
return new NewsLoader(this, uriBuilder.toString());
}
//loader callback method: onLoadFinished()
#Override
public void onLoadFinished(Loader<List<News>> loader, List<News> newslist) {
Log.i(LOG_TAG, "TEST: OnLoadFinished() called");
// Hide loading indicator because the data has been loaded
View loadingIndicator = findViewById(R.id.loading_indicator);
loadingIndicator.setVisibility(View.GONE);
// Set empty state text to display "No news found."
mEmptyStateTextView.setText(R.string.no_news_items);
//clear the adapter of previous earthquake data
mAdapter.clear();
//if there is a valid list of news objects, add them to the adapter's data set.
//this will trigger the list view to update
if (newslist!=null && !newslist.isEmpty()){
mAdapter.addAll(newslist); //to test empty state, comment this line
}
}
//loader callback method: onLoadReset()
#Override
public void onLoaderReset(Loader<List<News>> loader) {
Log.i(LOG_TAG, "TEST: onLoaderReset() called");
//reset the loader so we can clear out existing data
mAdapter.clear();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_activity);
// Get a reference to the LoaderManager, in order to interact with loaders.
LoaderManager loaderManager = getLoaderManager();
// Initialize the loader. Pass in the int ID constant defined above and pass in null for
// the bundle. Pass in this activity for the LoaderCallbacks parameter (which is valid
// because this activity implements the LoaderCallbacks interface).
Log.i(LOG_TAG, "TEST: calling initLoader() called");
loaderManager.initLoader(NEWS_LOADER_ID, null, this);
// Find a reference to the listview in the layout
ListView newsListView = (ListView) findViewById(R.id.list);
//to display the textview when it's empty
mEmptyStateTextView = (TextView)findViewById(R.id.empty_view);
newsListView.setEmptyView(mEmptyStateTextView);
// Create a new adapter that takes a list of news items as the input
mAdapter = new NewsAdapter(this, new ArrayList<News>());
// set the adapter on the listview so that the list can be populated in the user interface
newsListView.setAdapter(mAdapter);
//code to open url when tapping on an item in the list view
newsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//find the news-item that was clicked on
News currentNews = mAdapter.getItem(position);
//get the string url associated with current news item (using the getUrl() helper method
// and convert it into a Uri object to pass to the intent constructor
Uri newsUri = Uri.parse(currentNews.getUrl());
// create a new intent to view the news uri
Intent websiteIntent = new Intent(Intent.ACTION_VIEW, newsUri);
//launch new activity with this intent (aka open new link associated with that list item)
startActivity(websiteIntent);
}
});
// Get a reference to the ConnectivityManager to check state of network connectivity
ConnectivityManager connMgr = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
// Get details on the currently active default data network
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
// If there is a network connection, fetch data
if (networkInfo != null && networkInfo.isConnected()) {
// Initialize the loader. Pass in the int ID constant defined above and pass in null for
// the bundle. Pass in this activity for the LoaderCallbacks parameter (which is valid
// because this activity implements the LoaderCallbacks interface).
loaderManager.initLoader(NEWS_LOADER_ID, null, this);
} else {
//otherwise display error
//First, hide loading indicator so the error message will be visible
View loadingIndicator = findViewById(R.id.loading_indicator);
loadingIndicator.setVisibility(View.GONE);
//update empty state with no connection error message
mEmptyStateTextView.setText(R.string.no_internet_connection);
}
}
}
The error I get while compiling is:
error: incompatible types: NewsLoader cannot be converted to Loader<List>
return new NewsLoader(this, uriBuilder.toString());
This is the NewsLoader class
package com.example.newsapp;
import android.content.Context;
import android.util.Log;
import androidx.loader.content.AsyncTaskLoader;
import java.util.List;
public class NewsLoader extends AsyncTaskLoader<List<News>> {
/* Tag for log messages */
private static final String LOG_TAG = NewsLoader.class.getName();
/*Query URL*/
private String mUrl;
// Constructor: Constructs a new NewsLoader based on context of activity, and url to load from
public NewsLoader(Context context, String url) {
super(context);
mUrl = url;
}
#Override
protected void onStartLoading() {
Log.i(LOG_TAG, "TEST: onStartLoading() called");
forceLoad();
}
//this is on a background thread
#Override
public List<News> loadInBackground() {
Log.i(LOG_TAG, "TEST: loadInBackground() called");
if (mUrl == null) {
return null;
}
// Perform the network request, parse the response, and extract a list of earthquakes.
List<News> earthquakes = QueryUtils.fetchEarthquakeData(mUrl);
return earthquakes;
}
}
Any idea what I might be doing wrong?
OK I have a news program that gets data via JSON. It has a custom adapter which includes mainly strings and an image. At the top of the screen there is a search button and editText for the search criteria. That is not functioning yet, but will be easily fixed later. Anyway it works fine, but when I rotate the screen the data disappears and you have to press the search button again. But then really it never does reload the data. I'm hoping it's something in the main activity so I included it here. Any help would be appreciated!
package com.example.android.newsreport;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.Loader;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.List;
public class NewsReportActivity extends AppCompatActivity
implements LoaderCallbacks<List<Story>> {
private static final String LOG_TAG = NewsReportActivity.class.getName();
private String NewsApiUrl ="http://newsapi.org/v2/top-headlines?sources=bbc-news&apiKey=021abbfbc4de4c33b1644057d5f2be58";
/**
* Constant value for the Book loader ID. We can choose any integer.
* This really only comes into play if you're using multiple loaders.
*/
private static final int BOOK_LOADER_ID = 1;
private static String TAG = "MainActivity";
/** Adapter for the list of books */
private StoryAdapter mAdapter;
private StoryAdapter saveStateAdapter;
/** TextView that is displayed when the list is empty */
private TextView mEmptyStateTextView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_activity);
ListView newsListView = (ListView) findViewById(R.id.list);
mEmptyStateTextView = (TextView) findViewById(R.id.empty_view);
newsListView.setEmptyView(mEmptyStateTextView);
// Create a new adapter that takes an empty list of books as input
mAdapter = new StoryAdapter(this, new ArrayList<Story>());
// Set the adapter on the {#link ListView}
// so the list can be populated in the user interface
newsListView.setAdapter(mAdapter);
/// End Button search
// Set an item click listener on the ListView, which sends an intent to a web browser
// to open a website with more information about the selected book.
newsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
// Find the current book that was clicked on
Story currentstory = mAdapter.getItem(position);
// Convert the String URL into a URI object (to pass into the Intent constructor)
Uri newsUri = Uri.parse(currentstory.getPreviewLink());
// Create a new intent to view the book URI
Intent websiteIntent = new Intent(Intent.ACTION_VIEW, newsUri);
// Send the intent to launch a new activity
startActivity(websiteIntent);
}
});
View loadingIndicator = findViewById(R.id.loading_indicator);
loadingIndicator.setVisibility(View.GONE);
}
#Override
public Loader<List<Story>> onCreateLoader(int i, Bundle bundle) {
// Create a new loader for the given URL
return new StoryLoader(this, NewsApiUrl);
}
#Override
public void onLoadFinished(Loader<List<Story>> loader, List<Story> bookworms) {
// Hide loading indicator because the data has been loaded
View loadingIndicator = findViewById(R.id.loading_indicator);
loadingIndicator.setVisibility(View.GONE);
// Set empty state text to display "No books found."
mEmptyStateTextView.setText(R.string.no_stories);
// Clear the adapter of previous book data
mAdapter.clear();
// If there is a valid list of {#link Book}s, then add them to the adapter's
// data set. This will trigger the ListView to update.
if (bookworms != null && !bookworms.isEmpty()) {
mAdapter.addAll(bookworms);
//mAdapter.notifyDataSetChanged();
}
}
#Override
public void onLoaderReset(Loader<List<Story>> loader) {
// Loader reset, so we can clear out our existing data.
mAdapter.clear();
}
public void goQuery(View view){
EditText mySearchTextView = findViewById(R.id.searchEdit);
if (mySearchTextView.getText().length() < 1) return;
// ListView newsListView = (ListView) findViewById(R.id.list);
// TextView bookwormTextView = (TextView) findViewById(R.id.empty_view);
View loadingIndicator = findViewById(R.id.loading_indicator);
loadingIndicator.setVisibility(View.VISIBLE);
NewsApiUrl = "http://newsapi.org/v2/top-headlines?sources=bbc-news&apiKey=021abbfbc4de4c33b1644057d5f2be58";
ConnectivityManager connMgr = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
// Get details on the currently active default data network
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
// Get a reference to the LoaderManager, in order to interact with loaders.
android.app.LoaderManager loaderManager = getLoaderManager();
loaderManager.destroyLoader(BOOK_LOADER_ID);
mAdapter.clear();
mAdapter.notifyDataSetChanged();
// If there is a network connection, fetch data
if (networkInfo != null && networkInfo.isConnected()) {
// Initialize the loader. Pass in the int ID constant defined above and pass in null for
// the bundle. Pass in this activity for the LoaderCallbacks parameter (which is valid
// because this activity implements the LoaderCallbacks interface).
loaderManager.initLoader(BOOK_LOADER_ID, null, this);
} else {
// Otherwise, display error
// First, hide loading indicator so error message will be visible
loadingIndicator.setVisibility(View.GONE);
// Update empty state with no connection error message
mEmptyStateTextView.setText(R.string.no_internet_connection);
}
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mySearchTextView.getWindowToken(), 0);
}
}
How do I put the Loader, onLoadFinish, and onLoadReset into "public void onClick(View view) {"
This is the Code I have in my main activity I need to put my loader that is in the oncreate section into my onclicklistener.
I put the init loader in a public class called "load" and was able to call that, but the other public classes I am having a hard time putting in my onclicklistener.
Any help would be appreciated.
package com.example.android.googlebooks;
import android.app.LoaderManager;
import android.content.Context;
import android.content.Intent;
import android.content.Loader;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class BooksActivity extends AppCompatActivity
implements LoaderManager.LoaderCallbacks<List<Books>> {
private static final String LOG_TAG = BooksActivity.class.getName();
/**
* URL for books data from the USGS dataset
*/
private static String GOOGLEBOOKS_REQUEST_URL =
"https://www.googleapis.com/books/v1/volumes?q=";
/**
* Constant value for the book loader ID. We can choose any integer.
* This really only comes into play if you're using multiple loaders.
*/
private static final int BOOKS_LOADER_ID = 1;
/**
* Adapter for the list of books
*/
private BookAdapter mAdapter;
/** TextView that is displayed when the list is empty */
/**
* TextView that is displayed when the list is empty
*/
private TextView mEmptyStateTextView;
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.i(LOG_TAG, "TEST: Book Activity onCreate() called");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_books);
Button mButton = (Button) findViewById(R.id.button);
EditText mEdit = (EditText) findViewById(R.id.editText);
assert mEdit != null;
final Editable mEditText = mEdit.getText();
assert mButton != null;
mButton.setOnClickListener(
new View.OnClickListener() {
public void onClick(View view) {
GOOGLEBOOKS_REQUEST_URL = "https://www.googleapis.com/books/v1/volumes?q=" + mEditText;
load();
Toast.makeText(getApplicationContext(),
GOOGLEBOOKS_REQUEST_URL, Toast.LENGTH_LONG).show();
}
});
mEmptyStateTextView = (TextView) findViewById(R.id.empty_view);
// Find a reference to the {#link ListView} in the layout
ListView bookListView = (ListView) findViewById(R.id.list);
// Create a new adapter that takes an empty list of books as input
mAdapter = new BookAdapter(this, new ArrayList<Books>());
// Set the adapter on the {#link ListView}
// so the list can be populated in the user interface
bookListView.setAdapter(mAdapter);
bookListView.setEmptyView(mEmptyStateTextView);
// Set an item click listener on the ListView, which sends an intent to a web browser
// to open a website with more information about the selected books.
bookListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
// Find the current Books that were clicked on
Books currentBooks = mAdapter.getItem(position);
// Convert the String URL into a URI object (to pass into the Intent constructor)
Uri bookUri = Uri.parse(currentBooks.getmUrl());
// Create a new intent to view the books URI
Intent websiteIntent = new Intent(Intent.ACTION_VIEW, bookUri);
// Send the intent to launch a new activity
startActivity(websiteIntent);
}
});}
public void load() {
// Get a reference to the ConnectivityManager to check state of network connectivity
ConnectivityManager connMgr = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
// Get details on the currently active default data network
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
// If there is a network connection, fetch data
if (networkInfo != null && networkInfo.isConnected()) {
// Get a reference to the LoaderManager, in order to interact with loaders.
LoaderManager loaderManager = getLoaderManager();
// Initialize the loader. Pass in the int ID constant defined above and pass in null for
// the bundle. Pass in this activity for the LoaderCallbacks parameter (which is valid
// because this activity implements the LoaderCallbacks interface).
loaderManager.initLoader(BOOKS_LOADER_ID, null, this);
} else {
// Otherwise, display error
// First, hide loading indicator so error message will be visible
View loadingIndicator = findViewById(R.id.loading_spinner);
loadingIndicator.setVisibility(View.GONE);
// Update empty state with no connection error message
mEmptyStateTextView.setText(R.string.no_internet_connection);
new BooksLoader(this, GOOGLEBOOKS_REQUEST_URL);
}
}
public Loader<List<Books>> onCreateLoader(int i, Bundle bundle) {
Log.i(LOG_TAG, "TEST: Book Activity Loader,list,books called");
return new BooksLoader(this, GOOGLEBOOKS_REQUEST_URL);
}
public void onLoadFinished(Loader<List<Books>> loader, List<Books> book) {
ProgressBar info = (ProgressBar) findViewById(R.id.loading_spinner);
assert info != null;
info.setVisibility(View.GONE);
// Set empty state text to display "No Books found."
mEmptyStateTextView.setText(R.string.no_books);
Log.i(LOG_TAG, "TEST: Books Activity onLoadFinished() called");
// Clear the adapter of previous book data
mAdapter.clear();
// If there is a valid list of {#link Books}s, then add them to the adapter's
// data set. This will trigger the ListView to update.
if (book != null && !book.isEmpty()) {
mAdapter.addAll(book);
}
}
#Override
public void onLoaderReset(Loader<List<Books>> loader) {
Log.i(LOG_TAG, "TEST: Books Activity onLoaderReset() called");
// Loader reset, so we can clear out our existing data.
mAdapter.clear();
}
}
I am getting image urls using google image search apis. i use okhttp to connect to the url. this is done in my activity's oncreate method. in the oncreate() method of fragment i get all the image urls and store them in mSmallIMagesUrl, in the oncreateView() i try to display the images using a customized adapter. but when i run my app, hit the search button, it shows a blank page, when i go back and click the search button(there is always text in the search box) it shows images of the the previous search text. i dont know why it this,below is my code, please let me know what i am missing. thanks
package com.paveynganpi.allsearch;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.support.annotation.Nullable;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBar;
import android.support.v4.app.Fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.os.Build;
import android.widget.AbsListView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.GridView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.squareup.okhttp.Call;
import com.squareup.okhttp.Callback;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.w3c.dom.Text;
import java.io.IOException;
import java.util.ArrayList;
public class ImageGrid extends ActionBarActivity {
private static final String TAG = ImageGrid.class.getSimpleName();
protected int start = 0;//variable to change pages from google Api
//contains extra images urls to supply to ... when need
protected static ArrayList<ArrayList<String>> mBigImagesUrls = new ArrayList<ArrayList<String>>();
//contains image urls to inject into gridview
protected static ArrayList<String> mSmalImagesUrls = new ArrayList<>();
protected static String mEditedString;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_grid);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment())
.commit();
}
for (int start = 0; start < 2; start++) {
if (isNetworkAvailable()) {
//using okHttp library to connect to imagesUrl and retrieve JSON Data
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(getImagePage(start)).
build();
Call call = client.newCall(request);
//runs the below code asynchronously
call.enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
Log.v(TAG, "error from request");
}
#Override
public void onResponse(Response response) throws IOException {
try {
String jsonData = response.body().string();
//Log.v(TAG, jsonData);
if (!response.isSuccessful()) {
alertUserAboutError();
} else {
// mSmalImagesUrls = getCurrentDetails(jsonData);
mBigImagesUrls.add(getCurrentDetails(jsonData));
Log.d(TAG, mBigImagesUrls.size() + " big size");
Log.d(TAG, mSmalImagesUrls.size() + " small size");
}
} catch (IOException | JSONException e) {
Log.e(TAG, "Exception caught :", e);
}
}
});
} else {
Toast.makeText(this, "Network is unavailable", Toast.LENGTH_LONG).show();
}
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_image_grid, menu);
return true;
}
#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.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
private String getImagePage(int start) {
return "https://ajax.googleapis.com/ajax/services/search/images?v=1.0&q="
+ mEditedString + "&rsz=8&start=" + start;
}
//get data
private ArrayList<String> getCurrentDetails(String jsonData) throws JSONException {
JSONObject jsonObject = new JSONObject(jsonData);
JSONObject responseData = jsonObject.getJSONObject("responseData");
ArrayList<String> localList = new ArrayList<String>();
JSONArray results = responseData.getJSONArray("results");
for (int i = 0; i < results.length(); i++) {
localList.add(results.getJSONObject(i).getString("url"));
}
return localList;
}
//An AlertDialog to display to user when an error occurs
private void alertUserAboutError() {
AlertDialogFragment dialog = new AlertDialogFragment();
dialog.show(getFragmentManager(), "error_dialog");
}
//checks if user is connected to a network
private boolean isNetworkAvailable() {
ConnectivityManager cm =
(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isAvailable = false;
if (activeNetwork != null && activeNetwork.isConnectedOrConnecting()) {
isAvailable = true;
}
return isAvailable;
}
#Override
public void onBackPressed() {
super.onBackPressed();
Log.d(TAG,"back was pressed");
mBigImagesUrls.clear();
mSmalImagesUrls.clear();
}
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {
protected GridView mGridView;//reference to gridview in fragment_image_grid.xml
static String mEditedString;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, mBigImagesUrls.size() + " final big size");
Log.d(TAG, mSmalImagesUrls.size() + "final small size");
for(int i =0;i<mBigImagesUrls.size();i++){
for(int j =0;j<8;j++){
mSmalImagesUrls.add(mBigImagesUrls.get(i).get(j));
}
}
}
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.fragment_image_grid, container, false);
final ArrayList<String> testList = new ArrayList<String>();
//gets the edited string from MainActivity
Bundle args = getActivity().getIntent().getExtras();
mEditedString = args.getString("space");
mGridView = (GridView)rootView.findViewById(R.id.imagesGrid);//reference to gridview
ImagesGridAdapter adapter = new ImagesGridAdapter(getActivity(), mSmalImagesUrls);
mGridView.setAdapter(adapter);
return rootView;
}
}
}
Since you are making an asynchronous request, the response is not available immediately. You can display a ProgressBar while the list of images is being downloaded, and then display the list when it has completed downloading.
After that, notify your adapter that the dataset has changed.
I'm Getting data from my database and pushing them into ListView to view all the data.
Inside my ListView I also have a TextView with the text "Like" in it. Now when I click the like text it will change to "Liked" and I'm updating my like status in my database.
Now my problem is that when i click the "Like" text, the text changes to "Liked" and also it is updated in DB. But also when I scroll through the List View, I can notice other lists' Like text is also changed to Liked. I'm not sure what's going wrong.
I've been trying to get around this problem for quite some days but had no success.
This is my adapter code. At the bottom you can see my onClickListener for the textview
package com.mytestapp.myapp;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class ForumAdapter extends ArrayAdapter<DiscussionList> {
private static List<DiscussionList> items = null;
public ForumAdapter(Context context, List<DiscussionList> items) {
super(context, R.layout.custom_list, items);
this.items = items;
}
#Override
public int getCount() {
return items.size();
}
public static DiscussionList getModelPosition(int position) {
return items.get(position);
}
public void refill(List<DiscussionList> items) {
items.clear();
items.addAll(items);
notifyDataSetChanged();
}
public static class ViewHolder {
WebView mywebviewholder;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
View v = convertView;
if (v == null) {
LayoutInflater li = LayoutInflater.from(getContext());
v = li.inflate(R.layout.custom_list, null);
} else {
holder = (ViewHolder) convertView.getTag();
}
DiscussionList app = items.get(position);
if (app != null) {
TextView titleText = (TextView) v.findViewById(R.id.dscTitle);
TextView categoryText = (TextView) v.findViewById(R.id.dscCategory);
TextView descriptionText = (TextView) v
.findViewById(R.id.dscDescription);
TextView timeText = (TextView) v.findViewById(R.id.dscTime);
TextView idText = (TextView) v.findViewById(R.id.dscDiscId);
final TextView likeText = (TextView) v.findViewById(R.id.likeText1);
String like_Status = app.getLikeStatus();
titleText.setText(app.getTitle());
categoryText.setText(app.getCategory());
descriptionText.setText(app.getDescription());
timeText.setText(app.getTime());
idText.setText(app.getDiscId());
if (like_Status == "null") {
likeText.setText("Like");
} else {
likeText.setText("Liked");
}
final String dId = app.getDiscId();
// onClick for image button inside list view
likeText.setTag(new Integer(position));
likeText.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final Integer myposition = (Integer) view.getTag();
// Toast.makeText(getContext(), "" + dId,
// Toast.LENGTH_SHORT)
// .show();
likeText.setText("Liked");
MainActivity val = new MainActivity();
val.updateLikeTable(dId);
}
});
}
return v;
}
}
And also this is my MainActivity.java file where i update the likes in database
package com.mytestapp.myapp;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.os.StrictMode;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.Toast;
public class MainActivity extends ListActivity implements FetchDataListener {
public static String strTitle = "0", strCategory = "0",
strDescription = "0", strTime = "0", strDid = "0";
Collegemate_DB db = new Collegemate_DB(this);
int likeStatus = 1;
private ProgressDialog dialog;
public static String usId=null;
ImageButton imgButton;
List<DiscussionList> items = new ArrayList<DiscussionList>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_forum_topics);
usId = db.getCurrentuserId();
initView();
if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder()
.permitAll().build();
StrictMode.setThreadPolicy(policy);
}
/*
* On click listener to get values from DiscussionList class and send it
* to another activity when clicking on the list item
*/
ListView forumList = getListView();
forumList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// Get position of the clicked list item from adapter
String dTitle, dCategory, dDescription, dTime, dDid;
DiscussionList accessVar = ForumAdapter
.getModelPosition(position);
dTitle = accessVar.getTitle();
dCategory = accessVar.getCategory();
dDescription = accessVar.getDescription();
dTime = accessVar.getTime();
dDid = accessVar.getDiscId();
/*
* Storing the forum values in string and passing it to another
* activity
*/
String values[] = { dTitle, dCategory, dDescription, dTime,
dDid };
Intent i = new Intent(MainActivity.this, ForumFullView.class);
i.putExtra("sendData", values);
startActivity(i);
}
});
}
private void initView() {
// show progress dialog
Log.i("j","Inside Init");
dialog = ProgressDialog.show(this, "", "Loading...");
String url = "http://example.com/mypath/listData.php?currentUser_id="
+ usId;
Log.i("Fetch Url : ", url);
FetchDataTask task = new FetchDataTask(this);
task.execute(url);
}
#Override
public void onFetchComplete(List<DiscussionList> data) {
// dismiss the progress dialog
if (dialog != null)
dialog.dismiss();
// create new adapter
ListView forumList = getListView();
// set the adapter to list
ForumAdapter adapter = new ForumAdapter(this, data);
if (forumList.getAdapter() == null) {
//final ForumAdapter adapter = new ForumAdapter(this, data);
forumList.setAdapter(adapter);
} else {
((ForumAdapter) forumList.getAdapter()).refill(items);
}
// setListAdapter(adapter);
}
#Override
public void onFetchFailure(String msg) {
// dismiss the progress dialog
if (dialog != null)
dialog.dismiss();
// show failure message
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
}
public void updateLikeTable(String dId,List<DiscussionList> items) {
try {
String likeUrl = "http://example.com/mypath/createlike.php?discId="
+ dId + "&userId=" + usId + "&like_status=" + likeStatus;
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet();
request.setURI(new URI(likeUrl));
client.execute(request);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Any help is appreciated.
Thanks in advance
The problem you're having is that the ListView widget recycles its views if it can. Once a view is off the screen from scrolling, it goes onto a garbage heap so that when a new view scrolls into place it can be reused, rather than requiring a fresh one to be inflated from scratch. That recycled view is the convertView parameter in the getView() method. When your ListView is first populating, convertView will always be null, since the garbage pile has nothing in it, so you're forced to inflate new views, but subsequent calls will likely have that parameter as non-null.
The practical result of this is that when a clicked view that's been set to "Liked" gets recycled, the TextView is still there and still populated with "Liked" rather than the presumed default of "Like". So if you click a view, then scroll down so it goes off the screen, it'll come back around and cause the bug you're seeing.
What you'll probably want to do to fix this is to set the text of likeText within getView() every time, based on what it is in your database. If the post has been liked, set it to "Liked", and if it hasn't, set it to "Like". It should just be one more line, assuming you have easy access to whether or not the post is liked from your DiscussionList object.
P.S. As a side note, hard-coded strings are typically frowned upon in Android, so you may want to move your "Liked" string into the resource files. It's not really necessary unless you're planning to do translations, but it's still good practice.