I have the problem that every time I want to update my gridview in my fragment I get this error:
android.os.NetworkOnMainThreadException
What do I want to achive?
Show a loading gif in my layout until my fragment loads a list from firestore in the background and then set the data to the gridview Adapter and after that just hide the loading gif and show the gridview.
What did I try already?
I tried to fetch the data and set the Adapter in an AsynkTask and Background Thread but I still recive the same error. (Firestore itself is asynchron also)
When does the Error gets triggered?
public void updateAdapterView(JSONArray cards) {
//Error gets triggered here
grid.invalidateViews();
adapter.refresh(cards);
}
Fragment Activity:
//Set Layout for Fragment
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final FrameLayout view = (FrameLayout) inflater.inflate(R.layout.fragment_exchange, container, false);
loadinglayout = view.findViewById(R.id.loadinglayout);
gridlayout = view.findViewById(R.id.gridlayout);
//first time setting Adapter with empty items
adapter = new ExchangeListAdapter(getContext(), cards);
grid = view.findViewById(R.id.grid);
grid.setAdapter(adapter);
grid.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent exchangedetail = new Intent(getActivity(), ExchangeDetail.class);
//exchangedetail.putExtra("item", cardslist[+ position]);
getActivity().startActivity(exchangedetail);
}
});
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//get data from firestore
((MainActivity) getActivity()).getCardsList(new BaseAppCompatActivity.OnCardsFilledListener() {
#Override
public void onCardsFilled(final JSONArray cards) {
updateAdapterView(cards);
}
#Override
public void onError(Exception taskException) {
}
});
}
public void updateAdapterView(JSONArray cards) {
grid.invalidateViews();
adapter.refresh(cards);
}
GridView Adapter:
public class ExchangeListAdapter extends BaseAdapter {
private Context mContext;
private JSONArray cards;
public ExchangeListAdapter(Context c, JSONArray cards) {
mContext = c;
this.cards = cards;
}
#Override
public int getCount() {
int length = 0;
if(cards != null) {
length = cards.length();
}
return length;
}
#Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return null;
}
#Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
public void refresh(JSONArray cards)
{
this.cards = cards;
notifyDataSetChanged();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View grid;
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
grid = new View(mContext);
grid = inflater.inflate(R.layout.item_exchange_list, null);
ImageView imageView = grid.findViewById(R.id.grid_image);
URL url = null;
try {
url = new URL(cards.getJSONObject(position).getString("thumbnail"));
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
Bitmap bmp = null;
try {
bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
imageView.setImageBitmap(bmp);
} else {
grid = convertView;
}
return grid;
}
}
You are attempting to load an image from a URL on the main application thread inside your getView() method.
Add an image-loading library, such as Glide or Picasso, to your project. Then, use that library to load the image. A library like those can:
Handle placeholder images for a loading state
Do the network I/O on a background thread
Scale the image to fit your ImageView while taking up less memory
Deal with ImageView recycling, if the user scrolls while the image is still being loaded
Related
I am creating an android application in which there is a scenario where i have to add new items to the grid i am using an layout inflater to add new items to the grid the items gets added to the database sucessfully but the grid view is not refreshed once the item is added i have used notify dataset changed but it is not working can anyone tell me what i have to change in the existing code
Activity for the gridview:
listet = databaseHandlerOtherchgs.getAllproducttitle();
listet.add(new OtherChargesType("Plucking", bitMapData));
listet.add(new OtherChargesType("Loading", bitMaploading));
listet.add(new OtherChargesType("Add New", bitMapaddnew));
listcharges = new ArrayList<CustomizedCharge>());
adapter = new OtherChargesGridAdpater(SinglePageTransaction.this, listet);
gv.setAdapter(adapter);
gv.setExpanded(true);
GridAdapter:
public class OtherChargesGridAdpater extends BaseAdapter {
private Activity activity;
private LayoutInflater inflater;
private List<OtherChargesType> otherchargestypes;
DatabaseHandlerOtherChgs databaseHandlerOtherchgs;
public OtherChargesGridAdpater(Activity activity, List<OtherChargesType> otherchargestypes) {
this.activity = activity;
this.otherchargestypes = otherchargestypes;
}
#Override
public int getCount() {
return otherchargestypes.size();
}
#Override
public Object getItem(int i) {
return otherchargestypes.get(i);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertview, ViewGroup parent) {
if (inflater == null) {
inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
if (convertview == null) {
convertview = inflater.inflate(R.layout.row_othercharges_griditem, null);
}
OtherChargesType m = otherchargestypes.get(position);
byte[] outImage = m.getImage();
ByteArrayInputStream imageStream = new ByteArrayInputStream(outImage);
final Bitmap theImage = BitmapFactory.decodeStream(imageStream);
ImageView otherimages = (ImageView) convertview.findViewById(R.id.imageView1);
final TextView typename = (TextView) convertview.findViewById(R.id.textView1);
databaseHandlerOtherchgs=new DatabaseHandlerOtherChgs(activity);
// getting movie data for the row
typename.setText(m.getTypename());
otherimages.setImageBitmap(theImage);
notifyDataSetChanged();
return convertview;
}
public void updatedata(){
DatabaseHandlerOtherChgs databaseHandlerOtherChgs =new DatabaseHandlerOtherChgs(activity);
databaseHandlerOtherChgs.getAllproducttitle();
this.notifyDataSetChanged();
}
}
Use This code in Activity.. This code store your data.
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putString("MSG", "This is my message to be reloaded");
super.onSaveInstanceState(outState);
}
In Oncreate Method put this code
if (savedInstanceState != null) {
String str= savedInstanceState.getString("MSG");
Toast.makeText(this, str, Toast.LENGTH_LONG).show();
}
And for refresh your activity use follow code
finish();
startActivity(getIntent());
Make Changes as per your requirement.
I've spent two days trying to find a solution for this problem and couldn't find it anywhere. My problem is: I have a List View in which each item has 4 text views and an ImageView. I'm fetching data from a website using Volley. The data loads normally and when a I scroll down everything works perfectly. The problem is when I scroll back. When I'm scrolling back the images and textviews have a 0.5 delay to appear (First I see the picture of the last item) and then the content is shown. I've already tried recyclerview and listview with viewholder. This "flick" persists in both solutions.
public class BigCategoryListViewAdapter extends BaseAdapter{
private Context context;
private String[] smallCatsList;
private News news;
private ImageLoader imageLoader;
static class myViewHolder{
TextView smallCatName;
RelativeLayout spinner;
ImageView newsImageView;
TextView newsTitle;
TextView newsContent;
}
public BigCategoryListViewAdapter(Context context, String[] smallCatsList){
this.context = context;
this.smallCatsList = smallCatsList;
this.imageLoader = ImageLoader.getInstance();
if(!this.imageLoader.isInited()) { this.imageLoader.init(ImageLoaderConfiguration.createDefault(context));}
}
#Override
public int getCount() {
return smallCatsList.length;
}
#Override
public Object getItem(int position) {
return smallCatsList[position];
}
#Override
public long getItemId(int position) {return 0;} //modificar aqui
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
final myViewHolder mvh;
LayoutInflater inflater = (LayoutInflater) context.getSystemService(context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.bigcat_listview,null);
mvh = new myViewHolder();
mvh.smallCatName = (TextView) convertView.findViewById(R.id.bigcat_viewpager_smallcat);
mvh.spinner = (RelativeLayout) convertView.findViewById(R.id.spinner);
mvh.newsImageView = (ImageView) convertView.findViewById(R.id.bigcat_viewpager_imageView);
mvh.newsTitle = (TextView) convertView.findViewById(R.id.bigcat_viewpager_news_title);
mvh.newsContent = (TextView) convertView.findViewById(R.id.bigcat_viewpager_content);
convertView.setTag(mvh);
mvh.smallCatName.setText(smallCatsList[position]);
JSONObject params = new JSONObject();
try{
params.put("slug",smallCatsList[position]);
params.put("startIndex", 0);
params.put("endIndex", 0);
}catch (Exception e) {
Log.e(getClass().toString(), "Error setting params for communication with server");
e.printStackTrace();
}
Volley volley = Volley.getVolley(context);
volley.runRequest(Request.Method.POST, VolleyConstants.PROD_URL + VolleyConstants.NEWS_FETCH_ROUTE,
params, new Response.Listener<JSONObject>() {
//---------------------Volley------------------//
#Override
public void onResponse(JSONObject response) {
JSONArray responseArr = null;
try {
responseArr = response.getJSONArray("newsArr");
} catch (Exception e) {
e.printStackTrace();
}
for (int i = 0; i < responseArr.length(); i++) {
try {
JSONObject object = responseArr.getJSONObject(i);
String photoURLString = object.getString("photo");
String titleString = object.getString("title");
String publisherString = object.getString("publisher");
String dateString = object.getJSONObject("date").getString("date");
String contentString = object.getString("contents");
String urlString = object.getString("url");
news = new News(photoURLString, titleString, publisherString, dateString, contentString,urlString);
} catch (Exception e) {
news = new News("Unknown", "Unknown", "Unknown", "Unknown", "Unknown","Unknown");
}
}
//tv2.setText(news.getNewsDate());
mvh.newsContent.setText(news.getNewsContent());
mvh.newsTitle.setText(news.getNewsHead());
//---------------------Image Loading------------------//
try {
URL url = new URL(news.getNewsPic());
imageLoader.loadImage(news.getNewsPic(), new ImageLoadingListener() {
#Override
public void onLoadingStarted(String imageUri, View view) {
}
#Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
}
#Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
mvh.newsImageView.setImageBitmap(loadedImage);
mvh.spinner.setVisibility(View.GONE);
}
#Override
public void onLoadingCancelled(String imageUri, View view) {
}
});
}catch(Exception e){
mvh.newsImageView.setImageDrawable(context.getResources().getDrawable(R.drawable.nopic));
mvh.spinner.setVisibility(View.GONE);
}
}
}, new CustomErrorListener("ERROR"));
return convertView;
}
This is my adapter. My question is if there is a way to remove this delay to show the content? Or is my problem with network or the listview itself?
I just wanna load all the content for once and then the user can scroll up and down without having to refresh the content everytime.
You're loading the content every time getView is called, that's why it's loading like that. Separate the Volley request from the getView, it should be outside of the adapter entirely, not called upon every load of every piece of the list. You have your News Objects, you can pass an ArrayList to the adapter and fill it from there, that will fix your loading issue. Also, you're calling volley in the Main thread instead of a new thread, you should separate the two, and populate the ListView adapter upon completion of the content being loaded.
One, the ViewHolder pattern example should be something more like this:
if (convertView == null) {
convertView = inflater.inflate(R.layout.bigcat_listview,null);
mvh = new myViewHolder();
//... rest of your code
} else {
mvh = (myViewHolder) convertView.getTag();
//... rest of your code
}
Second, the major Volley loading stuff should not be in the adapter, but processed elsewhere beforehand then passed in. This example should help you out a lot.
How to make a boot image from a URL in the ListView, I tried to implement with this add-on , but the error takes NullPointerExeption. I also use this listView to display it in a horizontal format.This is my code appears when listView:
#Override
public void onCreate(Bundle savedInstanceState) {
...
image_list_icon = (ImageView) findViewById(R.id.image_fromlist);
HorizontalListView listview = (HorizontalListView) findViewById(R.id.listview);
listview.setAdapter(mAdapter);
}
private static String[] dataObjects = new String[]{ "Text #1",
"Text #2",
"Text #3","Text #3","Text #3" };
private BaseAdapter mAdapter = new BaseAdapter() {
ArrayList<Object> objects;
#Override
public int getCount() {
return Integer.MAX_VALUE;//
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View retval = LayoutInflater.from(parent.getContext()).inflate(R.layout.listview_leyout, null);
TextView title = (TextView) retval.findViewById(R.id.title);
title.setText(dataObjects[position%dataObjects.length]);
try {
if (title.getText() == "Text #1") {
ImageLoader imageLoader=new ImageLoader(context);
imageLoader.DisplayImage("http://www.learn2crack.com/wp-content/uploads/2014/06/Untitled.png", image_list_icon);
}
} catch (Exception e) {
Log.d("Ex",e.getMessage());
return null;
}
return retval;
}
};
I would recommend you using a library called Picasso. It takes care of a lot of issues surrounding images and memory management. Its also very easy to use.
For a list view, in your adapter getView() you simply do this:
ImageView myImageView = (ImageView)view.findViewById(R.id.myImage)
Picasso.with(context)
.load(imageUrl)
.into(myImageView);
See http://square.github.io/picasso/
I made a custom base adapter class in which I pass the context of activity and a JSONArray. I use this JSONArray data to set all the views. But the notifyDataSetChanged() on the adapter is not working. Do I have to pass a string array or arraylist of strings in the adapter for notifyDataSetChanged() to work? Will it not work if I pass JSONArray?
My Custom Adapter class code:
public class InboxMessagesAdapter extends BaseAdapter {
Activity act;
SharedPreferences prefs;
LayoutInflater Inflater;
JSONArray arrayOfMessages;
public InboxMessagesAdapter(Activity a, JSONArray arry) {
act = a;
arrayOfMessages = arry;
prefs = PreferenceManager.getDefaultSharedPreferences(a);
Inflater = (LayoutInflater) act
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return arrayOfMessages.length();
}
#Override
public Object getItem(int arg0) {
return arg0;
}
#Override
public long getItemId(int arg0) {
return arg0;
}
#Override
public View getView(int arg0, View arg1, ViewGroup arg2) {
View v1 = arg1;
try {
final int x = arg0;
JSONObject js = arrayOfMessages.getJSONObject(arg0);
v1 = Inflater.inflate(R.layout.element_inbox, null);
TextView titleOfMessage = (TextView) v1
.findViewById(R.id.msg_inbox_title);
TextView timeOfMessage = (TextView) v1
.findViewById(R.id.msg_inbox_time);
final CheckBox selectMessage = (CheckBox) v1
.findViewById(R.id.msg_inbox_check_button);
ImageView attachment = (ImageView) v1
.findViewById(R.id.msg_inbox_attachment);
ImageView starStatus = (ImageView) v1
.findViewById(R.id.msg_inbox_addtofav);
if (js.getString("StarStatus").equals("0"))
starStatus.setImageResource(R.drawable.btn_add_to_fav_normal);
else
starStatus.setImageResource(R.drawable.btn_add_to_fav_pressed);
if (js.getString("AttachmentStatus").equals("0"))
attachment.setVisibility(View.GONE);
titleOfMessage.setText(js.getString("MessageSubject"));
String arr[] = js.getString("MessageDate").split(" ");
try {
timeOfMessage.setText(arr[1] + arr[2]);
} catch (Exception e) {
timeOfMessage.setText(js.getString("MessageDate"));
}
v1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
try {
Intent in = new Intent(act, MessageActivity.class);
in.putExtra("data", arrayOfMessages.getJSONObject(x)
.toString());
act.startActivity(in);
} catch (Exception e) {
e.printStackTrace();
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
return v1;
}
}
you must be updating a different object of arrayOfMessages create a function in adapter like:
public void setArrayOfMessages(JSONArray arrayOfMessages){
this.arrayOfMessages=arrayOfMessages;
}
and before calling notifyDataSetChanged all this:
adapter.setArrayOfMessages(object);
adapter.notifyDataSetChanged();
Better take your data from Json Array into an Arraylist or List and then apply notifyDataSetChanged.
I'm trying to create a gridview that gives the possibility to the user to create a new child clicking on the last item on the gridview. I have a problem redrawing the view. In fact after the user creates a new child through an alertdialog I need to refresh the gridview to make this child visible. I've been looking for a solution for a while and everything I found is:
adapter.notifyDataSetChanged();
gridView.invalidateViews();
gridView.setAdapter(adapter);
Which is part of my code but doesn't,at least apparently, do anything. The code below is just the onCreateView() from the PlaceholderFragment class in the android template for an activty with swiping tabs. The app development here is still at the beginning but I'm already stuck with this issue..
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.fragment_one, container, false);
final GridView gridView = (GridView) rootView.findViewById(R.id.gridview);
final GridAdapter adapter =new GridAdapter(getActivity());
gridView.setAdapter(adapter);
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
if(position==0){
LayoutInflater inflater = getActivity().getLayoutInflater();
View newidea= inflater.inflate(R.layout.newidea, null);
final TextView txtname = ((TextView) newidea.findViewById(R.id.name));
final TextView txtdesc = ((TextView) newidea.findViewById(R.id.desc));
final Spinner spinner=((Spinner)newidea.findViewById(R.id.spinner));
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setIcon(R.drawable.type_add)
.setPositiveButton("Create", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
String name = txtname.getText().toString();
String desc = txtdesc.getText().toString();
DataReceiver dr = new DataReceiver(getActivity().getBaseContext(), name, desc,Integer.toString(spinner.getSelectedItemPosition()));
dr.writeFile();
dialog.dismiss();
adapter.notifyDataSetChanged();
gridView.invalidateViews();
gridView.setAdapter(adapter);
}
}).setNegativeButton("Forgot the idea?", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.dismiss();
}
}).setView(newidea)
.setTitle("New Idea");
AlertDialog dialog = builder.create();
dialog.show();
} else {
Toast.makeText(
getActivity(),
((TextView) v.findViewById(R.id.grid_item_label))
.getText(), Toast.LENGTH_SHORT).show();
}
}
});
return rootView;
}
}
UPDATE after code posting requests.
Here is the GridAdapter. Everything works fine, the only thing is that I still have to close and reopen the app to see the gridview redrawn and updated.
public class GridAdapter extends BaseAdapter {
private Context context;
boolean filesyet;
File f[];
public GridAdapter(Context context) {
this.context = context;
filesyet=filesYet();
}
#Override
public Object getItem(int i) {
return null;
}
#Override
public long getItemId(int i) {
return 0;
}
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View gridView;
if (convertView == null) {
gridView = inflater.inflate(R.layout.griditem, null);
TextView title = (TextView) gridView
.findViewById(R.id.grid_item_label);
LinearLayout background = (LinearLayout) gridView
.findViewById(R.id.grid_item);
if(position==0){
title.setText("");
Drawable d= context.getResources().getDrawable(R.drawable.type_add);
background.setBackground(d);
}else {
title.setText(f[position].getName());
Drawable d;
int selected=0;
try {
byte[] buffer = new byte[1];
new FileInputStream(f[position]).read(buffer,0,buffer.length);
selected=Integer.valueOf(new String(buffer));
} catch (FileNotFoundException e){
e.printStackTrace();
} catch (IOException e){
e.printStackTrace();
}
switch(selected){
case 1:
d=context.getResources().getDrawable(R.drawable.type_app);
break;
case 2:
d=context.getResources().getDrawable(R.drawable.type_engi);
break;
case 3:
d=context.getResources().getDrawable(R.drawable.type_event);
break;
default:
d=context.getResources().getDrawable(R.drawable.eureka);
break;
}
background.setBackground(d);
}
} else {
gridView = (View) convertView;
}
return gridView;
}
#Override
public int getCount() {
if (filesyet) {
return f.length;
} else {
return 1;
}
}
Boolean filesYet(){
try {
String path = context.getFilesDir().toString();
this.f = new File(path).listFiles();
System.out.println(f.toString());
return true;
} catch (NullPointerException e){
System.out.println("NULL");
return false;
}
}
Thanks for helping
You only load a list of files when the adapter is created in the constructor. As you are not creating a new constructor the filesYet() method is not called again and the file list is not updated.
To make this update you could override the notifydatasetchanged function in the adapter
#Override
public void notifyDataSetChanged (){
filesyet = filesYet();
super.notifyDataSetChanged();
}