Where to Async in PagerAdatper - android

My fragment activity used this adapter to display all the result in a listview.
The page will have a title tab and each tab will have the list result. Now the list result I read from internal storage and each time I swipe to next page it will have slight lag or delay, so I am thinking implementing ASYNCTask inside this pageradapter so the experience will be better but I have no idea where to implement. Could you guys point me out??
public class ViewPagerAdapter extends PagerAdapter
{
public ViewPagerAdapter( Context context )
{
//This is where i get my title
for (HashMap<String, String> channels : allchannel){
String title = channels.get(KEY_TITLE);
titles[acc] = title;
acc++;
}
scrollPosition = new int[titles.length];
for ( int i = 0; i < titles.length; i++ )
{
scrollPosition[i] = 0;
}
}
#Override
public String getPageTitle( int position )
{
return titles[position];
}
#Override
public int getCount()
{
return titles.length;
}
#Override
public Object instantiateItem( View pager, final int position )
{
String filename = null;
ListView v = new ListView( context );
final ArrayList<HashMap<String, String>> items = new ArrayList<HashMap<String, String>>();
DatabaseHandler db = new DatabaseHandler(context);
filename = openFile("PAGE1"); //OpenFile function is read file from internal storage
switch(position){
case 0:
filename = openFile("PAGE1");
break;
case 1:
filename = openFile("PAGE2");
break;
case 2:
filename = openFile("PAGE3");
break;
}
try{
//Use json to read internal storage file(Page1/Page2) and display all the result on the list
}
}catch(Exception e){
}
ListAdapter listadapter=new ListAdapter(context, items);
v.setAdapter( listadapter );
((ViewPager)pager ).addView( v, 0 );
return v;
}
#Override
public void destroyItem( View pager, int position, Object view )
{
( (ViewPager) pager ).removeView( (ListView) view );
}
#Override
public boolean isViewFromObject( View view, Object object )
{
return view.equals( object );
}
#Override
public void finishUpdate( View view )
{
}
#Override
public void restoreState( Parcelable p, ClassLoader c )
{
if ( p instanceof ScrollState )
{
scrollPosition = ( (ScrollState) p ).getScrollPos();
}
}
#Override
public Parcelable saveState()
{
return new ScrollState( scrollPosition );
}
#Override
public void startUpdate( View view )
{
}
}

I would suggest doing it like this
#Override
public Object instantiateItem( View pager, final int position )
{
//Some other work related to instantiation of item
AsyncTask<Uri, Void, Bitmap> mLoadTask = new AsyncTask<Uri, Void, Bitmap>() {
//async task for loading images in background
#Override
protected void onPostExecute(Bitmap result) {
pageview[position].setImageBitmap(result);
//post in ui thread
//this way you have a reference object for each item(using position)
//and hence you can start several tasks at the same time
}
#Override
protected Bitmap doInBackground(Uri... params) {
Bitmap bitmap=getBitmap(params[0]);
//here getBitmap returns the bitmap using uri arguement
return bitmap;
}
};
mLoadTask.execute(Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "" + image_id));
}
This way each item has its own thread and you need not worry no matter how many items get loaded.
EDIT One more thing, you can return pageview[position] from this method right after the AsyncTask code, this way the View will appear blank(smooth scrolling achieved) until AsyncTask completes the background work. But it would still be able to set the Bitmap to the correct View because it has reference inside onPostExecute

I'm doing it in constructor, and it's working fine. Example:
public MyAdapter(Context context) {
this.context = context;
try {
whatIneed = new DoSomeAsyncStuff(this.context).execute().get();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

Related

About NotifyDataSetChanged

I've been working on adapter and tried to update it with notifydatasetchanged. But the problem I face is that after i successfully update the adapter where I can see the changes have been made in the listview, it will then change back to previous data after I return from other fragment. I've heard that adapter will reuse the convert view with same data which cause the problem, but I don't know how to fix this. I've searched for the answer but couldn't find anything similar as most of the answers were all about declaring a new adapter.
Below is part of my code:
1.refresh method in fragment
public void toggleRefreshInBackground(ArrayList<HashMap<String, Object>> list) {
dialog_list.clear();
dialog_list.addAll(list);
adapter.update(list);
}
2.getView() of my adapter
public class DialogListAdapter extends BaseAdapter {
Context context;
private ImageLoader imageLoader;
private static LayoutInflater inflater;
private ArrayList<HashMap<String, Object>> member_list;
private ArrayList<HashMap<String, Object>> member_list2;
private SparseBooleanArray mSelectedItemsIds;
private ArrayList<Integer> selectedItemPosition;
//private static final int droidGreen = Color.parseColor("#A4C639");
//private BadgeView badge;
Resources res;
String sorting;
public DialogListAdapter(Context c,
ArrayList<HashMap<String, Object>>content, String temp_sorting) {
inflater=(LayoutInflater)c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.context=c;
this.member_list = content;
this.member_list2 = new ArrayList<>();
this.member_list2.addAll(this.member_list);
res=c.getResources();
this.sorting = temp_sorting;
mSelectedItemsIds = new SparseBooleanArray();
selectedItemPosition = new ArrayList<>();
}
#Override
public int getCount() {
// TODO Auto-generated method stub
return member_list.size();
}
#Override
public HashMap<String, Object> getItem(int position) {
// TODO Auto-generated method stub
return member_list.get(position);
}
#Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
public void update(ArrayList<HashMap<String,Object>> obj) {
member_list2.clear();
member_list2.addAll(obj);
notifyDataSetChanged();
}
public final class DialogListItem{ // view holder
public TextView read_count;
public TextView nameView;
public TextView numberView;
public TextView statusView;
public ImageView thumb_image;
public TextView timeView;
public TextView senderView;
public ImageView tick;
//public BadgeView badge;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
int index;
if(sorting.equals("D")){
index = member_list.size()-position-1;
}else{
index = position;
}
View row = convertView;
DialogListItem dialogItem=null;
String textbuff_time = null;
String textbuff_mess = null;
String textbuff_sender = null;
if (convertView==null){
row=inflater.inflate(R.layout.dialog_list_single, null);
dialogItem=new DialogListItem();
dialogItem.read_count=(TextView)row.findViewById(R.id.readcount);
dialogItem.nameView=(TextView)row.findViewById(R.id.member_name);
dialogItem.numberView=(TextView)row.findViewById(R.id.tab2number);
dialogItem.statusView=(TextView)row.findViewById(R.id.tab2status);
dialogItem.thumb_image=(ImageView)row.findViewById(R.id.tab2image);
dialogItem.timeView=(TextView) row.findViewById(R.id.tab2date);
dialogItem.senderView=(TextView) row.findViewById(R.id.tab2sender);
dialogItem.tick = (ImageView) row.findViewById(R.id.tick);
row.setTag(dialogItem);
}
else{
dialogItem=(DialogListItem) row.getTag();
}
if (mSelectedItemsIds.get(position)) {
row.setBackgroundColor(context.getResources().getColor(R.color.item_selected));
} else {
row.setBackgroundColor(context.getResources().getColor(R.color.item_unselected));
}
HashMap<String,Object>items;
items=member_list.get(index);
dialogItem.nameView.setText((String)items.get(DBhelper.MEMBER_NAME));
dialogItem.nameView.setTypeface(null,Typeface.BOLD);
dialogItem.nameView.setSingleLine(true);
dialogItem.nameView.setMaxLines(1);
dialogItem.nameView.setEms(13);
dialogItem.nameView.setEllipsize(TruncateAt.END);
dialogItem.numberView.setText((String)items.get(DBhelper.MEMBER_NUMBER));
String textbuff=(String)items.get(DBhelper.MEMBER_LMESSAGE);
if (!TextUtils.isEmpty(textbuff)){
textbuff_time=textbuff.substring(0, 19);
textbuff_mess=textbuff.substring(20);
int bound=textbuff_mess.indexOf("½");
if(bound!=-1){
textbuff_sender=textbuff_mess.substring(0,bound);
textbuff_mess=textbuff_mess.substring(bound+2);
}
else
textbuff_sender="";
dialogItem.statusView.setText(textbuff_mess);
if (textbuff_sender.equals(context.getResources().getString(R.string.myself))) {
dialogItem.senderView.setVisibility(View.GONE);
dialogItem.tick.setVisibility(View.VISIBLE);
} else {
textbuff_sender = textbuff_sender + ": ";
dialogItem.senderView.setVisibility(View.VISIBLE);
dialogItem.senderView.setText(textbuff_sender);
dialogItem.tick.setVisibility(View.GONE);
}
try {
dialogItem.timeView.setText(Utility.relativeTime(row.getContext(), textbuff_time));
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else{
dialogItem.statusView.setText("");
dialogItem.senderView.setText("");
dialogItem.timeView.setText("---");
}
dialogItem.statusView.setTextColor(Color.GRAY);
dialogItem.statusView.setTypeface(null,Typeface.NORMAL);
dialogItem.timeView.setTextColor(Color.GRAY);
dialogItem.timeView.setTypeface(null,Typeface.NORMAL);
dialogItem.senderView.setTextColor(Color.GRAY);
dialogItem.senderView.setTypeface(null,Typeface.NORMAL);
dialogItem.senderView.getPaint().setTextSkewX(-0.25f);
String sformat=" "+String.valueOf(items.get(DBhelper.MEMBER_SIZE))+" ";
dialogItem.read_count.setText(sformat);
int badge_number=(Integer)items.get(DBhelper.MEMBER_SIZE);
if(badge_number>0){
dialogItem.read_count.setVisibility(View.VISIBLE);
dialogItem.statusView.setTextColor(Color.BLACK);
dialogItem.statusView.setTypeface(null,Typeface.BOLD);
dialogItem.timeView.setTextColor(Color.BLACK);
dialogItem.timeView.setTypeface(null,Typeface.BOLD);
dialogItem.senderView.setTextColor(Color.BLACK);
dialogItem.senderView.setTypeface(null,Typeface.BOLD);
dialogItem.senderView.getPaint().setTextSkewX(-0.25f);
}
else{
dialogItem.read_count.setVisibility(View.GONE);
}
StringBuffer link=new StringBuffer(Register.get_current_server_address());
if(items.get(DBhelper.MEMBER_TYPE).equals(res.getString(R.string.broadcast_chat))){
dialogItem.nameView.setTextColor(row.getResources().getColor(R.color.tab4_title));
link.append("uniappSystemFile.asp");
link.append("?file=").append("broadcast.png");
//imageLoader.DisplayImage(link.toString(), dialogItem.thumb_image);
Picasso.with(context).load(link.toString()).transform(new CircleTransform()).resize(200,200).into(dialogItem.thumb_image);
dialogItem.read_count.setVisibility(View.GONE); // disable broadcast badge
}
Picasso.with(context).load(R.drawable.ic_contact).resize(200,200).into(dialogItem.thumb_image);
Picasso.with(context).load(R.drawable.ic_notif).resize(200, 200).into(dialogItem.thumb_image);
}else{
link2 = new IMService(context.getApplicationContext()).group_image_thumb_get(tab2.myphone, (String) items.get(DBhelper.MEMBER_NUMBER));
dialogItem.nameView.setTextColor(row.getResources().getColor(R.color.tab3_title));
//imageLoader.DisplayImage(link2, dialogItem.thumb_image);
Picasso.with(context).load(link2).transform(new CircleTransform()).resize(200, 200).placeholder(R.drawable.ic_group_circle_black_48dp).error(R.drawable.ic_group_circle_black_48dp).into(dialogItem.thumb_image);
}
}
public void setSearchPattern(String pattern) {
filterListByPattern(pattern.trim());
this.notifyDataSetChanged();
}
private void filterListByPattern(String searchPattern) {
member_list.clear();
for (HashMap<String,Object> info : member_list2) {
boolean add = true;
do {
if (searchPattern == null || searchPattern.equals("")) {
break;
}
if (info.get(DBhelper.MEMBER_NAME).toString().toLowerCase().contains(searchPattern)) {
break;
}
add = false;
} while (false);
if (add) {
member_list.add(info);
}
}
}
public void resetList() {
this.member_list2 = new ArrayList<HashMap<String,Object>>();
this.member_list2.addAll(this.member_list);
this.notifyDataSetChanged();
}
public void remove(Object object) {
member_list.remove(object);
notifyDataSetChanged();
}
public void toggleSelection(int position) {
selectView(position, !mSelectedItemsIds.get(position));
}
public void removeSelection() {
mSelectedItemsIds = new SparseBooleanArray();
selectedItemPosition = new ArrayList<>();
notifyDataSetChanged();
}
public void selectView(int position, boolean value) {
if (value) {
mSelectedItemsIds.put(position, value);
selectedItemPosition.add(position);
} else {
mSelectedItemsIds.delete(position);
selectedItemPosition.remove(selectedItemPosition.indexOf(position));
}
notifyDataSetChanged();
}
public int getSelectedCount() {
return mSelectedItemsIds.size();
}
public SparseBooleanArray getSelectedIds() {
return mSelectedItemsIds;
}
public ArrayList<Integer> getItemPosition() {
return selectedItemPosition;
}
}
So, my question is why would my adapter and listview show the old data after I return from other fragment to this fragment, and how to fix this?
P.S. please don't tell me to declare a new adapter like this
adapter = new MyAdapter(list);
adapter.notifyDataSetChanged();
I know it works, but it's not a good and appropriate approach since it's a heavy operation and you don't even need to use notifyDataSetChanged after declaring a new adapter.
Many thanks
Try putting setRetainInstance(true);
inside your fragment.
If above does not work then try with onSaveInstanceState() as below -
Declare at top, Parcelable state;
Save listview state, state = listview.onSaveInstanceState(); inside onpause() of fragment or manually while changing fragment.
Then inside onCreateView check state is null or not. If null then set adapter / get data, if not then set
listview.onRestoreInstanceState(state);
On the other hand, Each time you load fragment, ArrayList will start off empty. So you need to be careful to save data.

error while showing image from server into viewPager android with picasso

I am trying to slide image in viewPager, images sliding well automatically while I am using images from drawable folder. But while I am tying to show images from server to be slided from server image url its showing error something like this:
The application's PagerAdapter changed the adapter's contents without calling PagerAdapter#notifyDataSetChanged!
I have tried to solve this problem by googling but I could not solve that. I need help. My code is below:
activity work:
String imageListQuery=null;
try {
imageListQuery=URLEncoder.encode("getChatRooms", "UTF-8") + "=" + URLEncoder.encode("getChatRooms", "UTF-8");
backgroundTask=new AsyncTaskClass(context_NavigationActivity, new AsyncTaskResult() {
#Override
public void AsyncTaskResult(String result) throws JSONException {
if(result!=null){
pageViewerAdapter.notifyDataSetChanged();
}
JSONObject js = new JSONObject(result);
// Log.v("ResDFrJ",js.toString());
JSONArray arr = js.getJSONArray("result");
for (int i = 0; i < arr.length(); i++) {
JSONObject tmpOb = new JSONObject(arr.optString(i));
UsersListShowClass tmpClass = new UsersListShowClass();
tmpClass.setProPicUrl_users(InfoSetClass.getBaseUrl()+tmpOb.getString("profile_pic_id"));
imageList.add(i,tmpClass);
}
}
});
backgroundTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"POST","mobileApi/chatRooms",imageListQuery);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
viewPager=(ViewPagerCustomDuration)findViewById(R.id.pageViewerId);
viewPager.setScrollDurationFactor(15);
pageViewerAdapter=new PageViewerAdapter(context_NavigationActivity,imageList);
NUM_PAGES=pageViewerAdapter.getCount();
viewPager.setAdapter(pageViewerAdapter);
pageViewerAdapter.notifyDataSetChanged();
final Handler handler = new Handler();
final Runnable update = new Runnable() {
public void run() {
if (currentPage == NUM_PAGES - 1) {
currentPage = 0;
}
viewPager.setCurrentItem(currentPage++, true);
}
};
new Timer().schedule(new TimerTask() {
#Override
public void run() {
handler.post(update);
}
}, 100, 5000);
adapter:
public class PageViewerAdapter extends PagerAdapter {
private Context context;
private ArrayList<UsersListShowClass> imageList;
UsersListShowClass usersListShowClass;
public PageViewerAdapter(Context context,ArrayList<UsersListShowClass> imageList){
this.context=context;
this.imageList=imageList;
}
#Override
public int getCount() {
if(imageList != null){
return imageList.size();
}
return 0;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return (view==(LinearLayout)object);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
LayoutInflater layoutInflater=(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view=layoutInflater.inflate(R.layout.page_viewer_adapter_layout,container,false);
ImageView imageView=(ImageView)view.findViewById(R.id.videoViewId);
usersListShowClass=imageList.get(position);
Picasso.with(context).load(usersListShowClass.getProPicUrl_users()).error(R.drawable.ic_circle_24dp).
placeholder(R.drawable.ic_circle_24dp).into(imageView);
container.addView(view);
return view;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((LinearLayout) object);
}
#Override
public int getItemPosition(Object object) {
return PageViewerAdapter.POSITION_NONE;
}
}
i know its already solved almost similar error in stackoverflow, but in my case maybe a little different. please need help.
Try to add:
pageViewerAdapter.notifyDataSetChanged();
after:
imageList.add(i,tmpClass);

Android : How to update a single row in a Recycler view list

I have an app that calling an API its resulting around 500 rows creation.In my app the row content can update in the detail page. So after update is there any possibility to update the row in the Recycler View without calling the API again.
Activity
AsyncHttpClient client = new AsyncHttpClient();
client.get("URL", new AsyncHttpResponseHandler() {
#Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
try {
String jsonStr = new String(responseBody, "UTF-8");
if (jsonStr != null) {
try {
JSONArray jsonArray = new JSONArray(jsonStr);
JSONObject cStatus = jsonArray.getJSONObject(jsonArray.length()-1);
for (int i = 0; i < jsonArray.length(); i++)
{
JSONObject c = jsonArray.getJSONObject(i);
String firstName = c.getString("firstName");
String subDistributerId = c.getString("subDistributerId");
SubdistributorItem item = new SubdistributorItem();
item.setSubDistributorName(firstName);
item.setSubDistributorId(subDistributerId);
ubdistributorItemList.add(item);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
Collections.sort(subdistributorItemList, new Comparator<SubdistributorItem>() {
public int compare(SubdistributorItem o1, SubdistributorItem o2) {
return o1.getSubDistributorName().compareToIgnoreCase(o2.getSubDistributorName());
}
});
adapter = new SubdistributorRecyclerAdapter(getActivity(),subdistributorItemList);
mRecyclerView.setAdapter(adapter);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
});
Adapter Class
public class SubdistributorRecyclerAdapter extends RecyclerView.Adapter<SubdistributorListRowHolder> {
private List<SubdistributorItem> subdistributorItems;
private Context mContext;
private ArrayList<SubdistributorItem> arraylist;
public SubdistributorRecyclerAdapter(Context context, List<SubdistributorItem> subdistributorItems) {
this.subdistributorItems = subdistributorItems;
this.mContext = context;
this.arraylist = new ArrayList<SubdistributorItem>();
this.arraylist.addAll(subdistributorItems);
}
#Override
public SubdistributorListRowHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.subdistributor_list_item, null);
SubdistributorListRowHolder mh = new SubdistributorListRowHolder(v);
layout_subdistributor.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(final View v) {
Intent i = new Intent(mContext, SubdistributorDetail.class);
Log.e("Tag shop ", "ShopKeeper Detail called");
i.putExtra("subDistributorStatus", txt_RechargeSubdistributor.getText().toString());
i.putExtra("subDistributorId", txt_subDistributorId.getText().toString());
mContext.startActivity(i);
}
});
return mh;
}
#Override
public void onBindViewHolder(SubdistributorListRowHolder subDistributorListRowHolder, int i) {
SubdistributorItem subdistributorItem = subdistributorItems.get(i);
Log.e("Tag ", "adapter "+ subdistributorItem.getSubDistributorName());
}
#Override
public int getItemCount() {
return (null != subdistributorItems ? subdistributorItems.size() : 0);
}
}
So can any one please help me to update a single row in a list without calling the API again.
Try using this method, to update a single row.
notifyItemChanged(int position)
Update item in your list subdistributorItems. Then call adapter.notifyItemChanged(int position). You can get a position in ClickListener using
layout_subdistributor.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(final View v) {
if (mh.getAdapterPosition() != RecyclerView.NO_POSITION) {
int position = mh.getAdapterPosition();
// edit your object by calling subdistributorItems.get(position)
}
}
});
Change this
public static class SubdistributorListRowHolder extends RecyclerView.ViewHolder {
private TextView textView_alphabet;
private TextView textView_name;
private TextView textView_tag;
private ImageView imageViewUserImage;
private ImageView imageViewMoreButton;
private LinearLayout linearLayoutMainContent;
public ViewHolder(View itemLayoutView) {
super(itemLayoutView);
textView_alphabet = (TextView) itemLayoutView.findViewById(R.id.textView_alphabet);
textView_name = (TextView) itemLayoutView.findViewById(R.id.textView_name);
textView_tag = (TextView) itemLayoutView.findViewById(R.id.textView_tag);
imageViewUserImage = (ImageView) itemLayoutView.findViewById(R.id.imageViewUserImage);
linearLayoutMainContent = (LinearLayout) itemLayoutView.findViewById(R.id.linearLayoutMainContent);
}
}
#Override
public void onBindViewHolder(SubdistributorListRowHolder subDistributorListRowHolder, int i) {
SubdistributorItem subdistributorItem = subdistributorItems.get(i);
Log.e("Tag ", "adapter "+ subdistributorItem.getSubDistributorName());
subDistributorListRowHolder.itemLayoutView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.e("Tag ", "Position "+ i);
}
});
}
Over here itemLayoutView is the mail layout which is clickable
Modify the SubdistributorListRowHolder like this put all your layout component and find view by id.
getViewForPosition(int position)
Obtain a view initialized for the given position. This method should be used by RecyclerView.LayoutManager implementations to obtain views to represent data from an RecyclerView.Adapter.
The Recycler may reuse a scrap or detached view from a shared pool if one is available for the correct view type. If the adapter has not indicated that the data at the given position has changed, the Recycler will attempt to hand back a scrap view that was previously initialized for that data without rebinding.
description here
Its simple.
Make the changes in the ArrayList that is bonded with the recycler view and call notifyDataSetChanged() on the Recycler View.

Loading text in recycler view with card view is too slow

I have a problem with the recycler view and card view. I'm using asynctask to get info from API, and for now I'm getting only a name - which means, I display in my card view only a text view. however, when I'm loading the list, it is awfully slow. in the log cat I can see that the app is getting the data pretty fast, but it takes a lot of time to show it in the recycler view.
I'm adding few samples - from the adapter of the recycler view and the fragment that holds the recycler view in. maybe I did something wrong in the adapter.
Thank you for your help!
Adapter:
public class PlacesListAdapter extends RecyclerView.Adapter<PlacesListAdapter.ListViewHolder>{
ArrayList<PlaceItem> items;
Context context;
public PlacesListAdapter(Context context,ArrayList<PlaceItem> placeItems){
this.context = context;
this.items = placeItems;
}
public void swap(ArrayList<PlaceItem> places){
items.clear();
items.addAll(places);
notifyDataSetChanged();
}
#Override
public ListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(context).inflate(R.layout.card_view, parent, false);
return new ListViewHolder(v);
}
#Override
public void onBindViewHolder(ListViewHolder holder, int position) {
PlaceItem item = items.get(position);
holder.bindData(item);
}
#Override
public int getItemCount() {
return items.size();
}
public class ListViewHolder extends RecyclerView.ViewHolder{
TextView title;
PlaceItem placeItem;
public ListViewHolder(View itemView) {
super(itemView);
title = (TextView) itemView.findViewById(R.id.txtTitlePlace);
}
public void bindData(PlaceItem item){
this.placeItem = item;
title.setText(placeItem.getTitle());
}
}
}
Fragment:
public class FragmentListPlaces extends Fragment implements View.OnClickListener {
ArrayList<PlaceItem> placeItems;
PlacesListAdapter adapter;
RecyclerView list;
EditText editName;
public FragmentListPlaces() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_list_places, container, false);
editName = (EditText) v.findViewById(R.id.editPlaceName);
v.findViewById(R.id.btnGetLocations).setOnClickListener(this);
v.findViewById(R.id.btnSearchByText).setOnClickListener(this);
placeItems = new ArrayList<>();
placeItems.add(new PlaceItem("Example"));
adapter = new PlacesListAdapter(getContext(), placeItems);
list = (RecyclerView) v.findViewById(R.id.placesList);
list.setLayoutManager(new LinearLayoutManager(getContext()));
list.setAdapter(adapter);
return v;
}
#Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btnGetLocations:
GetUserLocation location = new GetUserLocation();
location.getLocation(getActivity());
adapter.swap(placeItems);
break;
case R.id.btnSearchByText:
// this is the method loading data with user input
String getNameFromUser = editName.getText().toString();
searchPlaceByText(getNameFromUser);
adapter.swap(placeItems);
break;
}
}
public void searchPlaceByText(String place){
// instantiate the asynctask here
LocationDetailsByText locationDetailsByText = new LocationDetailsByText(placeItems);
locationDetailsByText.execute("http://api.v3.factual.com/t/places-il?q=" + place + "&KEY=AFvDryDJmPkkgXohbpFdqkRQelT9w0HKtyEqXy3G");
}
Loading of data from the web:
public class LocationDetailsByText extends AsyncTask<String, Void, String> {
ArrayList<PlaceItem> placeItems = new ArrayList<>();
public LocationDetailsByText(ArrayList<PlaceItem> places){
this.placeItems = places;
}
#Override
protected String doInBackground(String... params) {
StringBuilder result = new StringBuilder();
BufferedReader reader;
HttpURLConnection connection = null;
URL url;
String query = (params[0]);
try {
url = new URL(query);
connection = (HttpURLConnection)url.openConnection();
if(connection.getResponseCode() != 200){
return "Error!";
}
reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line = "";
while((line = reader.readLine())!= null){
result.append(line);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
connection.disconnect();
}
return result.toString();
}
#Override
protected void onPostExecute(String s) {
PlaceItem placeItem;
try {
JSONObject root = new JSONObject(s);
JSONObject response = root.getJSONObject("response");
JSONArray data = response.getJSONArray("data");
for(int i = 0; i < data.length(); i++){
JSONObject getData = data.getJSONObject(i);
String title = getData.getString("name");
placeItem = new PlaceItem(title);
placeItems.add(placeItem);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
Apart from the issue that #M G pointed out that messes up the behavior in general.
you have 2 other major flaws that I can see of which 1) is affecting you mostly.
1) you do all the Json parsing + moving the data to your POJO(PlaceItem[]) onPostExecute. this is wrong!
this can be very heavy on computation
this way you create 2 many intermediate objects lots of GC
I suggest move these to background and use Gson.
2) it seems that you do lots of network calls that could happen quite frequently. This needs better managing of concurrent requests, network connections, threads, streams and data arrays obtained form the network stream. This can cause lots of GC.
I would recommend to use some networking library made for this purpose such as retrofit, volley or jus. These all can handle also parsing network data straight to your POJO in the background and minimizing GC and performance in general.
Few issues with your code
searchPlaceByText(getNameFromUser);
adapter.swap(placeItems);
adapter.swap(placeItems); starts right after you start your AsyncTask but you didn't download anything yet. This is wrong. You should remove adapter.swap(placeItems); from here and do something like this instead:
#Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btnGetLocations:
GetUserLocation location = new GetUserLocation();
location.getLocation(getActivity());
adapter.swap(placeItems);//also here probably
break;
case R.id.btnSearchByText:
// this is the method loading data with user input
String getNameFromUser = editName.getText().toString();
searchPlaceByText(getNameFromUser);
break;
}
}
public void searchPlaceByText(final String place) {
// instantiate the asynctask here
LocationDetailsByText locationDetailsByText = new LocationDetailsByText(placeItems) {
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
adapter.swap(placeItems);
}
};
locationDetailsByText.execute("http://api.v3.factual.com/t/places-il?q=" + place + "&KEY=AFvDryDJmPkkgXohbpFdqkRQelT9w0HKtyEqXy3G");
}
Next thing is that you clear your list
items.clear();
items.addAll(places);
Which is basically also removing everything from your placeItems because erlier in this class you set this.items = placeItems;. So in PlacesListAdapter just do
public void swap(ArrayList<PlaceItem> places){
notifyDataSetChanged();
}
In Your Asynctask, in OnPostExcute, at the end of it notify your adapter about the changes in your data, that's why you cant see your data unless you click on edit text again.

Android RecyclerView freezes when image loading is done using Picasso

In my android app, on homepage there is a recyclerview consisting cardviews as each item. First,I call ParseServer to get data on server and pass it to the recyclerview adapter. I pass image url along with other details to adapter class.
Inside my MainActivity.java
Parse.initialize(this, "key1", "key2");
ParsePush.subscribeInBackground("", new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
Log.d("com.parse.push", "successfully subscribed to the broadcast channel.");
} else {
Log.e("com.parse.push", "failed to subscribe for push", e);
}
}
});
if(haveNetworkConnection()){
new task().execute();
mRecyclerView = (RecyclerView)findViewById(R.id.my_recycler_view);
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
Class task which fetches data from server:
public class task extends AsyncTask<Void, Void, Void>{
ProgressDialog mProgressDialog;
#Override
protected void onPreExecute() {
super.onPreExecute();
rel.setVisibility(View.VISIBLE);
pro.setVisibility(View.VISIBLE);
titles.clear();
images.clear();
venues.clear();
timings.clear();
urgent.clear();
}
#Override
protected Void doInBackground(Void... params) {
ParseQuery<ParseObject> query = ParseQuery.getQuery("notices");
try {
List<ParseObject> ob = query.find();
for(ParseObject obj:ob){
String str = obj.getString("title");
String str1 = obj.getString("venue");
String str2 = obj.getString("timing");
ParseFile file = obj.getParseFile("images");
String str3 = obj.getString("urgent");
String url;
if(file!=null)
url = file.getUrl();
else{
url = "https://www.medidirect.com.au/MEDIstores/_images/no-thumb.png";
}
titles.add(str);
images.add(url);
venues.add(str1);
timings.add(str2);
urgent.add(str3);
}
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
AdapNotice mAdapter = new AdapNotice(MainActivity.this,titles,images,venues,timings,urgent);
if(titles.size()<1)
Toast.makeText(MainActivity.this, "Oops, Something went wrong. Please try again", 300).show();
mRecyclerView.setAdapter(mAdapter);
pro.setVisibility(View.GONE);
rel.setVisibility(View.GONE);
}
}
Inside AdapNotice class which is recyclerview adapter:
public class AdapNotice extends RecyclerView.Adapter<AdapNotice.ViewHolder>{
Activity activity;
ArrayList<String> _titles = new ArrayList<String>();
ArrayList<String> _images = new ArrayList<String>();
ArrayList<String> _venues = new ArrayList<String>();
ArrayList<String> _timings = new ArrayList<String>();
ArrayList<String> _urgents = new ArrayList<String>();
private int lastPosition = -1;
int pos;
public AdapNotice(Activity a, ArrayList<String> titles, ArrayList<String> images, ArrayList<String> venues, ArrayList<String> timings, ArrayList<String> urgent) {
activity = a;
_titles = titles;
_images = images;
_venues = venues;
_timings = timings;
_urgents = urgent;
}
public static class ViewHolder extends RecyclerView.ViewHolder{
// each data item is just a string in this case
public TextView title;
public ImageView imgview,urgent;
public TextView venue,venuetext,abctext;
public TextView timing,timingtext,abc1text;
public ViewHolder(View v) {
super(v);
title = (TextView) v.findViewById(R.id.title);
imgview = (ImageView) v.findViewById(R.id.img_notice);
venue = (TextView) v.findViewById(R.id.venue);
timing = (TextView) v.findViewById(R.id.timing);
venuetext = (TextView)v.findViewById(R.id.venuetext);
abctext = (TextView)v.findViewById(R.id.abctext);
timingtext = (TextView)v.findViewById(R.id.timingtext);
abc1text = (TextView)v.findViewById(R.id.abc1text);
urgent = (ImageView)v.findViewById(R.id.urgentstar);
}
}
#Override
public int getItemCount() {
// TODO Auto-generated method stub
if(_titles.size()<=0)
return 0;
return _titles.size();
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
// TODO Auto-generated method stub
try{
holder.title.setText(_titles.get(position));
if(_urgents.get(position).contains("yes")){
holder.urgent.setVisibility(View.VISIBLE);
}
pos = position;
if(!_venues.get(position).contentEquals("---")){
holder.venuetext.setVisibility(View.VISIBLE);
holder.venue.setVisibility(View.VISIBLE);
holder.abctext.setVisibility(View.VISIBLE);
holder.venue.setText(_venues.get(position));
}
if(!_timings.get(position).contentEquals("---")){
holder.timingtext.setVisibility(View.VISIBLE);
holder.timing.setVisibility(View.VISIBLE);
holder.abc1text.setVisibility(View.VISIBLE);
holder.timing.setText(_timings.get(position));
}
Target target = new Target() {
#Override
public void onPrepareLoad(Drawable arg0) {
// TODO Auto-generated method stub
Animation anim = AnimationUtils.loadAnimation(activity, R.anim.rotate);
holder.imgview.setImageResource(R.drawable.loading);
holder.imgview.startAnimation(anim);
}
#Override
public void onBitmapLoaded(Bitmap arg0, LoadedFrom arg1) {
// TODO Auto-generated method stub
holder.imgview.clearAnimation();
holder.imgview.setImageBitmap(arg0);
Log.i("completed", pos+" completed");
storeDB obj = new storeDB(_titles.get(pos), _venues.get(pos), _timings.get(pos), DbBitmapUtility.getBytes(arg0), _urgents.get(pos));
obj.execute();
}
#Override
public void onBitmapFailed(Drawable arg0) {
// TODO Auto-generated method stub
holder.imgview.clearAnimation();
holder.imgview.setImageResource(R.drawable.error);
}
};
Picasso.with(activity).load(_images.get(position)).into(target);
}catch(Exception e){
e.printStackTrace();
}
}
private class storeDB extends AsyncTask<Void, Void, Void>{
String title,venue,timing,urgent;
byte[] image;
public storeDB(String one, String two, String three, byte[] four, String five){
title = one;
venue = two;
timing = three;
image = four;
urgent = five;
}
#Override
protected Void doInBackground(Void... params) {
SQLiteHandler handler = new SQLiteHandler(activity);
handler.addNotice(title, venue, timing, image, urgent);
handler.close();
return null;
}
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int arg1) {
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.item, parent, false);
Animation animation = AnimationUtils.loadAnimation(activity, (arg1>lastPosition)?R.anim.up_from_bottom:R.anim.down_from_top);
v.startAnimation(animation);
ViewHolder vh = new ViewHolder(v);
return vh;
}
}
I pass the image url to picasso library which loads image into target. Till image is not fetched by picasso, I am showing another loading image and giving it rotate animation effect.
Sometimes, even if image is loaded by picasso, it is not shown in imageview.I know this because on clicking that imageview full image is loaded in another activity which works fine. The rotating animation stops and loaded image also not shown in imageview. Also, the main view freezes sometimes when imageview is being loaded.
So, how can i solve this issue and make my app smoother in operation?
Thanks in advance.
UPDATE I found out that freezing issue was because of the weak reference to target. I solved it with strong reference but how do I solve hanging issue ? App gets hanged for some time when I am scrolling .
You can use a simple approach
Picasso.with(context)
.load(url)
.placeholder(R.drawable.user_placeholder)
.error(R.drawable.user_placeholder_error)
.into(imageView);
placholder image will be shown before loading the image and error image will be shown if error occur
UPDATE
to store in a database just write this much boiler plate code
class MyTarget implements Target {
private ImageView imageView;
public MyTarget(ImageView imageView) {
this.imageView = imageView;
}
#Override
public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) {
this.imageView.setImageBitmap(bitmap);
//store your bitmap as you were storing
}
}
and then
Picasso.with(context)
.load(url)
.placeholder(R.drawable.user_placeholder)
.error(R.drawable.user_placeholder_error)
.into(new MyTarget(imageView));

Categories

Resources