I need help with a programming part in Android Studio.
I have a RecyclerView corresponding to a json array from my database. In this array are some informations given to fill the RecyclerView. Every json object has an URL for a corresponding file on a webserver too.
At this part I need your help. I need to fetch the URL from every json object and download the corresponding file to the device and set it to the RecyclerView row.
The URL in the json has the name: download_url (not in my code at the moment but in the json array.
The download state should be shown in the status bar with a progress view (I need to add some informations like the name to the notification). When the user clicks the row the PDF file should be shown in a PFD viewer.
The next thing is to delete these files from device. They should be deleted automatically if the user refreshs the RecyclerView and the given json don't has the file anymore.
The URL has the filename at the the end corresponding to the file on the server.
Thanks for your idea's. I'm a beginner and try to give my best to learn a lot from tutorials but this question is very hard and specific for me.
This is my Adapter:
public class PlansAdapter extends RecyclerView.Adapter<PlansAdapter.MyPlanHolder> {
private List<Plans> planList;
private final Context customContext;
public class MyPlanHolder extends RecyclerView.ViewHolder {
public TextView planTitle, planType, planDate;
public MyPlanHolder(View view) {
super(view);
planTitle = (TextView) view.findViewById(R.id.planTitle);
planType = (TextView) view.findViewById(R.id.planTyp);
planDate = (TextView) view.findViewById(R.id.planDate);
}
}
public PlansAdapter(List<Plans> planList, Context customContext) {
this.planList = planList;
this.customContext = customContext;
}
#Override
public MyPlanHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.plans_list_row, parent, false);
return new MyPlanHolder(itemView);
}
#Override
public void onBindViewHolder(MyPlanHolder holder, int position) {
final Plans plan = planList.get(position);
// Format timestamp to date
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = null;
String resultDate = null;
try {
date = fmt.parse(plan.getPlanDate());
SimpleDateFormat fmtOut = new SimpleDateFormat("dd.MM.yyyy");
resultDate = fmtOut.format(date);
} catch (Exception e) {
e.printStackTrace();
}
holder.planTitle.setText(plan.getPlanTitle());
holder.planType.setText(plan.getEatingPlanType() + " " + plan.getTrainingPlanType());
holder.planDate.setText("resultDate);
}
#Override
public int getItemCount() {
return planList.size();
}
}
This is my Request.class where I get the json Object:
public class PlansRequest extends StringRequest {
public static final String PLANS_REQUEST_URL = "http://hiddenwebserver.de/de/json";
private Map<String, String> params;
public PlansRequest(String loginEmail, Response.Listener<String> listener) {
super(Request.Method.POST, PLANS_REQUEST_URL, listener, null);
params = new HashMap<>();
params.put("loginEmail", loginEmail);
}
#Override
public Map<String, String> getParams() {
return params;
}
}
This is where I set the RecyclerView on my Fragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_training, container, false);
// Get Refresh Layout
swipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.training_swiperefresh);
// Connectivity manager for network check
final ConnectivityManager connectivityManager = (ConnectivityManager) getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
// Change color for the refresh layout
swipeRefreshLayout.setColorSchemeColors(Color.rgb(99, 195, 195));
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
// Check if network connection exists
if (connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState() == NetworkInfo.State.CONNECTED ||
connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState() == NetworkInfo.State.CONNECTED) {
// Get plans data from web
getPlansData();
} else {
// Error message if internet connection is not available
Toast.makeText(getActivity(), getString(R.string.noInternetConnection), Toast.LENGTH_LONG).show();
}
}
});
trainingRecyclerView = (RecyclerView) view.findViewById(R.id.training_recycler_view);
pAdapter = new PlansAdapter(planList, getContext());
RecyclerView.LayoutManager pLayoutManager = new LinearLayoutManager(getActivity().getApplicationContext());
trainingRecyclerView.setLayoutManager(pLayoutManager);
trainingRecyclerView.setItemAnimator(new DefaultItemAnimator());
trainingRecyclerView.setAdapter(pAdapter);
preparePlansData();
return view;
}
This is where I prepare the data from the json array:
private void preparePlansData() {
// Connectivity manager for network check
final ConnectivityManager connectivityManager = (ConnectivityManager) getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
// Check if network connection exists
if (connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState() == NetworkInfo.State.CONNECTED ||
connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState() == NetworkInfo.State.CONNECTED) {
// Get shared preferences
SharedPreferences plansPreferenceData = getActivity().getSharedPreferences("plansData", MODE_PRIVATE);
if (plansPreferenceData.contains("plansPreferenceData")) {
String plansData = plansPreferenceData.getString("plansPreferenceData", "").replace("\"success\":true,", "");
// Remove success string from jsonResponse
//plansData.replace("success\":true,", "");
Log.d("plansData", "Result:" + plansData);
Gson gson = new Gson();
Type type = new TypeToken<Map<String, Plans>>(){}.getType();
Map<String, Plans> myPlansMap = gson.fromJson(plansData, type);
planList.addAll(myPlansMap.values());
// Notify data changes
pAdapter.notifyDataSetChanged();
// Now we call setRefreshing(false) to signal refresh has finished
swipeRefreshLayout.setRefreshing(false);
} else {
getPlansData();
}
} else {
// Error message if internet connection is not available
Toast.makeText(getActivity(), getString(R.string.noInternetConnectionOnStart), Toast.LENGTH_LONG).show();
}
}
Related
I want to get string from arraylist inside oncreateview fragment but i cant figure itout since no position index has been pass. get(position) return error.
String price = arrayList.get(position).getPrice();
i need to get string price and settext for price.this is my main concern.
this values should return from arraylist.
this is response JSON array from volley using mysingleton.
Single Product Response: [{"price":"75","date":"2017-07-13 03:25:31","pk_i_id":"4"}]
this main activty fragment
public class MainActivityFragment extends Fragment {
private TextView product,price,date,title;
private String product_id;
ArrayList<ProductItem> arrayList = new ArrayList<>();
Context context;
public MainActivityFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_main_activity, container, false);
product = (TextView) view.findViewById(R.id.tv1);
title = (TextView) view.findViewById(R.id.tvTitle);
price = (TextView) view.findViewById(R.id.tvPrice);
date = (TextView) view.findViewById(R.id.tvDate);
if (getArguments() != null) {
Log.i(TAG, "getArgument is not null");
product_id = getArguments().getString("product_id");
ProductBackgroundTask productBackgroundTask = new ProductBackgroundTask(this.getActivity(), product_id);
arrayList = productBackgroundTask.getList();
String price = arrayList.get(position).getPrice();
// Log.d(TAG, "price: " + price);
product.setText(product_id);
// price.setText(price);
}else {
Log.i(TAG, "getArgument is null");
}
return view;
}
}
this is task to get arraylist using volley
public class ProductBackgroundTask {
private Context context;
ArrayList<ProductItem> arrayList = new ArrayList<>();
String json_url = "phpfile.php";
private String product_id;
public ProductBackgroundTask(Context context, String product_id) {
this.context = context;
this.product_id = product_id;
}
public ArrayList<ProductItem> getList(){
StringRequest stringRequest = new StringRequest(Request.Method.POST, json_url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Log.d(TAG, "Single Product Response: " + response);
try {
JSONArray jsonarr = new JSONArray(response);
for (int i = 0; i < jsonarr.length(); i++) {
JSONObject jsonobj = jsonarr.getJSONObject(i);
ProductItem productItem = new ProductItem(jsonobj.getString("price"), jsonobj.getString("date"), jsonobj.getInt("pk_i_id"));
arrayList.add(productItem);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}){
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String,String> params = new HashMap<>();
params.put("product_id", product_id);
return params;
}
};
MySingleton.getInstance(context).addToRequestQueue(stringRequest);
return arrayList;
}
}
and this is class of array list
public class ProductItem {
private String Price,Date;
private int ProductId;
public ProductItem(String Price, String Date, int ProductId){
this.setPrice(Price);
this.setDate(Date);
this.setProductId(ProductId);
}
public int getProductId() {
return ProductId;
}
public void setProductId(int productId) {
ProductId = productId;
}
public String getPrice() {
return Price;
}
public void setPrice(String price) {
Price = price;
}
public String getDate() {
return Date;
}
public void setDate(String date) {
Date = date;
}
Clearly in your oncreate you haven’t initialized the product item and you cannot parse the complete list.You can try two to solve this
1.Pass specific item number instead of position i.e
say if you want to show 4th item then position=3
2.Or write a loop like this to parse entire arrayList like this
for(ProductItem productItem:arrayList){
String price = productItem.getPrice();
// Log.d(TAG, "price: " + price);
product.setText(product_id);
price.setText(price);
}
Mistake you're doing is that in the MainActivityFragment your trying to assign the value to the arrayList even before the data is added to the arrayList in the ProductBackgroundTask-getList. That's the reason you are getting the list null all the time. Try to use interfaces.
1.Make your MainActivityFragment implement the interface.
2.Set the value to the interface method once you get the data from the server.
3.Get the data in the MainActivityFragment inside interface method and do all the operation you're doing inside the onCreateView method.
Now your arraylist will have the data whatever you received from the server.
Below is the link for the example on interfaces if you haven't used them before. He is doing exactly as your requirement.
https://www.justinmccandless.com/post/setting-up-a-callback-function-in-android/
Allow me. The arrayList that you return from getList isn't populated at the time you call String price = arrayList.get(position).getPrice();. The server call using volley takes some time to process and that's when the onResponse gets called. This happens AFTER you've returned the arrayList which is in fact empty.
The sequence of events is as follows.
• Call to arrayList = productBackgroundTask.getList(); which returns an empty ArrayList.
• String price = arrayList.get(position).getPrice();
Now after a while..
• onResponse inside getList() gets called.
Do you now see why it's empty?
Simple Solution: • Define a simple interface ProductListener alongside ProductBackgroundTask. (With only a single abstract method onProducts).
• Instantiate it inside the Fragment's onCreateView using an anonymous class and pass it to the constructor of ProductListener to save it for later use. Do whatever you want to do with the products inside the onProducts method. (Since that will be called with the actual data)
• Call its onProducts method with the data that's parsed and fetched inside the onResponse method.
ProductBackgroundTask code:
public class ProductBackgroundTask {
private Context context;
// I removed the instance ArrayList since that can be made
// local.
// Here, we add a reference to our callback interface as we can use it later.
private ProductListener listener;
String json_url = "http://192.168.43.55/android/v1/productList.php";
private String product_id;
// Instantiate this class using an additional listener argument
// which would be a concrete implementation of our interface.
public ProductBackgroundTask(Context context, String product_id, ProductListener listener) {
this.context = context;
this.product_id = product_id;
this.listener = listener;
}
// getList should not return anything,
// so I keep the return as void.
public void getList() {
StringRequest stringRequest = new StringRequest(Request.Method.POST, json_url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
ArrayList<ProductItem> arrayList = new ArrayList<>();
Log.d(TAG, "Single Product Response: " + response);
try {
JSONArray jsonarr = new JSONArray(response);
for (int i = 0; i < jsonarr.length(); i++) {
JSONObject jsonobj = jsonarr.getJSONObject(i);
ProductItem productItem = new ProductItem(jsonobj.getString("price"), jsonobj.getString("date"), jsonobj.getInt("pk_i_id"));
arrayList.add(productItem);
}
// Notice this line here, this is what
// calls the callback with the products.
listener.onProducts(arrayList);
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}){
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String,String> params = new HashMap<>();
params.put("product_id", product_id);
return params;
}
};
MySingleton.getInstance(context).addToRequestQueue(stringRequest);
}
}
// Callback interface, we would need a concrete implementation
// of this and pass that to the constructor of ProductBackgroundTask.
interface ProductListener {
void onProducts(ArrayList<ProductItem> products);
}
The code inside onCreateView:
ProductBackgroundTask productBackgroundTask = new ProductBackgroundTask(this.getActivity(), product_id, new ProductListener() {
// This method will be called with the needed products.
// Give an anonymous class implementation of our interface
// right here since we won't be using it anymore.
public void onProducts(ArrayList<ProductItem> products) {
// Get the price you want.
String str = arrayList.get(0).getPrice();
// Use str wherever necessary. Use the UI thread here if you need
// to change any visible elements on the screen.
}
});
// Simply call this method to get the ball rolling.
productBackgroundTask.getList();
This is a concrete implementation of the this answer and you won't be changing much code.
Please help me out to load more data from the server upon scrolling my RecyclerView . Here I have successfully created RecyclerView by loading data from my Mysql server by using volley string request.
Here is my code.
private void populateRecycleView() {
if (Utility.checkNetworkConnection(this)) {
final ProgressDialog progressDialog = new ProgressDialog(this);
progressDialog.setTitle("Searching...");
progressDialog.setMessage("Searching for the blood donor. Please wait a moment.");
progressDialog.setCancelable(false);
progressDialog.show();
StringRequest stringRequest = new StringRequest(Request.Method.POST, Constants.GET_DONORS_URL,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
progressDialog.dismiss();
try {
JSONArray jsonArray = new JSONArray(response);
int count = 0;
while (count < jsonArray.length()) {
JSONObject jsonObject = jsonArray.getJSONObject(count);
String firstName = jsonObject.getString("fName");
String secondName = jsonObject.getString("sName");
String email = jsonObject.getString("emailid");
String password = jsonObject.getString("pass");
String mobile = jsonObject.getString("mobile");
String bloodRt = jsonObject.getString("blood");
String age = jsonObject.getString("age");
String gender = jsonObject.getString("gender");
String country = jsonObject.getString("country");
String location = jsonObject.getString("location");
String latitude = jsonObject.getString("latitude");
String longitude = jsonObject.getString("longitude");
String profilePicFIleName = jsonObject.getString("picname");
String profilePicURL = jsonObject.getString("pic");
Donor donor = new Donor(firstName, secondName, email, password, mobile, bloodRt, age, gender,
country, location, latitude, longitude, profilePicFIleName, profilePicURL);
donorsList.add(donor);
count++;
}
donorsAdapter = new DonorsAdapter(FindDonorResult.this, donorsList);
recyclerView = (RecyclerView) findViewById(R.id.rv_search_result_donor);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(FindDonorResult.this));
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(donorsAdapter);
donorsAdapter.notifyDataSetChanged();
} catch (JSONException e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
progressDialog.dismiss();
Toast.makeText(FindDonorResult.this, "Active data network is not available.", Toast.LENGTH_LONG).show();
}
}
) {
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> params = new HashMap<>();
params.put("bloodGroup", bloodGroup);
return params;
}
};
NetworkRequestSingleTon.getOurInstance(this).addToRequestQue(stringRequest);
} else {
Utility.checkNetworkConnectionFound(this);
}
}
And this is my RecyclerView adapter...
public class DonorsAdapter extends RecyclerView.Adapter<DonorsAdapter.CustomViewHolder> {
private Context context;
private ArrayList<Donor> donorList;
private String bloodGroup;
public DonorsAdapter(Context context, ArrayList<Donor> donorList) {
this.context = context;
this.donorList = donorList;
}
#Override
public CustomViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.rv_blood_donors_result,
parent, false);
return new CustomViewHolder(view);
}
#Override
public void onBindViewHolder(CustomViewHolder holder, int position) {
final Donor donor = donorList.get(position);
String displayName = donor.getFirstName() + " " + donor.getSecondName();
holder.tvDisplayName.setText(displayName);
holder.tvEmailID.setText(donor.getEmail());
String userProfileURL = donor.getProfilePicURL();
if (!userProfileURL.equals("")) {
Picasso.with(context).load(userProfileURL).resize(80, 80).centerCrop().
into(holder.ivProfilePic);
} else {
holder.ivProfilePic.setImageResource(R.drawable.ic_person_white_24dp);
}
bloodGroup = donor.getBloodGroup();
if (bloodGroup.equals("A+"))
holder.ivBloodTypeDisplay.setImageResource(R.drawable.a_);
else if (bloodGroup.equals("A-"))
holder.ivBloodTypeDisplay.setImageResource(R.drawable.a_negative);
else if (bloodGroup.equals("B+"))
holder.ivBloodTypeDisplay.setImageResource(R.drawable.b_positive);
else if (bloodGroup.equals("B-"))
holder.ivBloodTypeDisplay.setImageResource(R.drawable.b_negative);
else if (bloodGroup.equals("O+"))
holder.ivBloodTypeDisplay.setImageResource(R.drawable.o_positive);
else if (bloodGroup.equals("O-"))
holder.ivBloodTypeDisplay.setImageResource(R.drawable.o_negative);
else if (bloodGroup.equals("AB+"))
holder.ivBloodTypeDisplay.setImageResource(R.drawable.ab_positive);
else if (bloodGroup.equals("AB-"))
holder.ivBloodTypeDisplay.setImageResource(R.drawable.ab_negative);
if(Utility.isNetworkEnabled){
holder.constraintLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(context, DisplayDonorDetails.class);
intent.putExtra("donor", donor);
context.startActivity(intent);
}
});
}else {
Toast.makeText(context, "Network not available.", Toast.LENGTH_SHORT).show();
}
}
#Override
public int getItemCount() {
if(donorList != null){
return donorList.size();
}else {
return 0;
}
}
public class CustomViewHolder extends RecyclerView.ViewHolder {
ImageView ivProfilePic, ivBloodTypeDisplay, ivArrow;
TextView tvDisplayName;
TextView tvEmailID;
ConstraintLayout constraintLayout;
public CustomViewHolder(View itemView) {
super(itemView);
ivProfilePic = (ImageView) itemView.findViewById(R.id.civ_user_profile_picture);
ivBloodTypeDisplay = (ImageView) itemView.findViewById(R.id.civ_user_blood_type_display);
ivArrow = (ImageView) itemView.findViewById(R.id.civ_arrow);
tvDisplayName = (TextView) itemView.findViewById(R.id.tvUserNameOnRV);
tvEmailID = (TextView) itemView.findViewById(R.id.tvEmailDisplayOnRV);
constraintLayout = (ConstraintLayout) itemView.findViewById(R.id.recycle_view_item_container);
}
}
}
Populate your donorsAdapter only with the 50 first elements of your donorsList, create a function that save the position of the latest element displayed and add other 50 donors to your adapter starting from the latest position saved when you need it.
Hope it helps.
EDIT
First create an emptyList:
List<Donor> subElements = new ArrayList<>();
and pass it to your adapter:
donorsAdapter = new DonorsAdapter(FindDonorResult.this, subElements);
Now you can create a method like this (you can call in onClick event for example):
private int LAST_POSITION = 0;
private int DONORS_NUM_TOSHOW = 50;
public void showMoreDonors(){
if(donarsList.size() > Last_postion+50){
List<Donor> tempList = new ArrayList<Donor>(donorsList.subList(Last_postion,Last_postion+DONORS_NUM_TOSHOW));
for(Donor a : tempList){
subElements.add(a);
}
Last_postion += DONORS_NUM_TOSHOW;
donorsAdapter.notifyDataSetChanged();
}else{
List<Donor> tempList = new ArrayList<Donor>(donorsList.subList(Last_postion,donorsList.size()));
for(Donor a : tempList){
subElements.add(a);
}
donorsAdapter.notifyDataSetChanged();
}
}
Remember to check when donorsList is over.
I didn't test it, but i hope it is usefull to understand the idea.
Finally, I sort this out. I have got an awesome tutorial from this blog.
http://android-pratap.blogspot.in/2015/06/endless-recyclerview-with-progress-bar.html.
I made some changes to populate the list because my data is on a remote server and by using volley library I fetched the data into the list. Remaining things are same.
I need to add my Items one by one to my RcyclerView for example add first item to recyclerView and show then add another item....
I have a volley that connect to a service then get me some data, in this volley I call a method :
private void makeJsonArryReq(String uri) {
userPointList = new ArrayList<>();
orginal = new ArrayList<>();
final JsonArrayRequest req = new JsonArrayRequest(uri,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
orginal = MarketingPoints_JSONParser.FeedOriginal(response.toString(), mUser_Code, ReportActivity.this);
userPointList = MarketingPoints_JSONParser.parseFeed(response.toString(), mUser_Code, ReportActivity.this);
FillList();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
VolleyLog.d("Custom Log Error", "Error: " + error.getMessage());
}
});
AppController.getInstance().addToRequestQueue(req, tag_json_arry);
}
when I call this method FillList(); it fill and show first item on RecyclerView,How can I listen to adapter for finalized and call FillList(); again for next item .
My FillList():
public void FillList() {
Geocoder geocoder = new Geocoder(activity, Locale.getDefault());
List<Address> addresses;
StepModel stp = null;
List<StepModel> Step = new ArrayList<>();
if (check < userPointList.size()) {
try {
addresses = geocoder.getFromLocation(userPointList.get(check).getLat(), userPointList.get(check).getLng(), 1);
stp = new StepModel();
stp.setsCity(addresses.get(0).getAdminArea());
stp.setsStreet(addresses.get(0).getThoroughfare());
stp.setSlat(userPointList.get(check).getLat());
stp.setSlng(userPointList.get(check).getLng());
stp.setSdate(userPointList.get(check).getDate());
stp.setStime(userPointList.get(check).getTime());
stp.setsCounts(userPointList.get(check).getCounts());
stp.setSprofilePhoto(userPointList.get(check).getProfilePhoto());
stp.setsUserCode(userPointList.get(check).getUserCode());
Step.add(stp);
} catch (IOException e) {
e.printStackTrace();
}
pointAdapter = new PointsList_Adapter(Step, ReportActivity.this,this, city, street);
Points_Recycler.setAdapter(pointAdapter);
pointAdapter.notifyDataSetChanged();
check++;
}else {
return;
}
}
Explain :
I connect to a service then I get a json then I convert it and store in a List.In my json I have two specific data (Lat and lng) that I should get name country and name street(For example 100 lat lng) then show on recyclerView but it get long time because I should fill recyclerView on by one.
My adapter :
public class PointsList_Adapter extends RecyclerView.Adapter<PointsList_Adapter.ViewHolder> {
int count = 0;
private List<StepModel> mpList;
public static Activity activity;
public static boolean flagItem = true;
public static boolean flagToast = true;
private mstep Mstep;
public PointsList_Adapter(List<StepModel> userCodeList, Activity activity) {
this.mpList = userCodeList;
this.activity = activity;
}
#Override
public PointsList_Adapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// create a new view
View itemLayoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.listpoints_cardview, null);
// create ViewHolder
ViewHolder viewHolder = new ViewHolder(itemLayoutView);
return viewHolder;
}
#Override
public void onBindViewHolder(PointsList_Adapter.ViewHolder viewHolder, final int position) {
String dateValue = mpList.get(position).getDate();
final String countValue = String.valueOf(mpList.get(position).getCounts());
final Double lat = mpList.get(position).getLat();
final Double lng = mpList.get(position).getLng();
final String userCode = mpList.get(position).getUserCode();
final String time = mpList.get(position).getTime();
final int counts = mpList.get(position).getCounts();
final String city = mpList.get(position).getCity();
final String street = mpList.get(position).getStreet();
viewHolder.txtDate.setText(dateValue);
viewHolder.txtPoints.setText(lat + " , " + lng);
viewHolder.txtTime.setText(time);
viewHolder.txtAddress.setText(city + " , " + street);
viewHolder.txtCount.setText(countValue);
}
#Override
public int getItemCount() {
return mpList.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView txtDate, txtPoints, txtTime, txtAddress, txtCount;
public LinearLayout lLstPoints;
public ViewHolder(View itemLayoutView) {
super(itemLayoutView);
txtDate = (TextView) itemLayoutView.findViewById(R.id.txtDate);
txtPoints = (TextView) itemLayoutView.findViewById(R.id.txtPoints);
txtTime = (TextView) itemLayoutView.findViewById(R.id.txtTime);
txtAddress = (TextView) itemLayoutView.findViewById(R.id.txtAddress);
txtCount = (TextView) itemLayoutView.findViewById(R.id.txtCount);
lLstPoints = (LinearLayout) itemLayoutView.findViewById(R.id.lLstPoints);
}
}
}
Please add below method in your adapter:
public reloadList(ArrayList<String> mpList) {
this.mpList.addAll(mpList);
}
When you want to update list or want to add one more item in your listview, use below code:
public void FillList() {
Geocoder geocoder = new Geocoder(activity, Locale.getDefault());
List<Address> addresses;
StepModel stp = null;
List<StepModel> Step = new ArrayList<>();
if (check < userPointList.size()) {
try {
addresses = geocoder.getFromLocation(userPointList.get(check).getLat(), userPointList.get(check).getLng(), 1);
stp = new StepModel();
stp.setsCity(addresses.get(0).getAdminArea());
stp.setsStreet(addresses.get(0).getThoroughfare());
stp.setSlat(userPointList.get(check).getLat());
stp.setSlng(userPointList.get(check).getLng());
stp.setSdate(userPointList.get(check).getDate());
stp.setStime(userPointList.get(check).getTime());
stp.setsCounts(userPointList.get(check).getCounts());
stp.setSprofilePhoto(userPointList.get(check).getProfilePhoto());
stp.setsUserCode(userPointList.get(check).getUserCode());
Step.add(stp);
} catch (IOException e) {
e.printStackTrace();
}
// Just update your list and call notifyDataSetChanged()
pointAdapter .reloadList(Step);
pointAdapter .notifyDataSetChanged();
check++;
}else {
return;
}
}
Initialize your adapter as below:
ArrayList<StepModel> userCodeList = new ArrayList<StepModel>();
pointAdapter = new PointsList_Adapter(userCodeList , activity) ;
Thank You.
After retrieving all data from server at once, inside Filllist() method you should use for, while or do..while loops to iterate your code.You have used if conditional loop that will run only once.
Else, you can create a method inside adapter to add item to your list and then call notifydatasetchanged() method there.
public void add(Object obj){
mArraylist.add(obj);
notifyDataSetChanged();
}
I am writing an app to pull movie data from the MovieDB api and display the information in a GridView.
When the app first loads I would expect the view to populate initially, but it does not. I have a sort option in the menu bar and when the sort option is set the first time the GridView is populated by movies in order of popularity, as it should initially, but regardless of what sort criteria is actually selected.
I have used the logs to determine that the correct data is being retrieved from the API and being processed properly, so I have to assume that the adapter is not updating the view properly.
Why isn't the view showing initially or updating as it should?
FilmFragment.java:
public class FilmFragment extends Fragment {
private ArrayList<FilmParcelable> filmParcels = new ArrayList<FilmParcelable>();
private ImageAdaptor mFilmAdaptor;
protected String[] sortOptions = {
"popularity.desc",
"vote_average.desc"
};
protected String sortBy = sortOptions[0];
private final String LOG_TAG = FilmFragment.class.getSimpleName();
public FilmFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
if (savedInstanceState == null || !savedInstanceState.containsKey("films")){
updateFilms();
mFilmAdaptor = new ImageAdaptor(getActivity(),filmParcels);
} else {
filmParcels = savedInstanceState.getParcelableArrayList("films");
mFilmAdaptor = new ImageAdaptor(getActivity(),filmParcels);
}
// allow fragment to handle menu events
setHasOptionsMenu(true);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
inflater.inflate(R.menu.filmfragment, menu);
}
public boolean onOptionsItemSelected(MenuItem item){
//Handle action bar item clicks. 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();
if (id == R.id.action_sort){
showSortDialog();
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onSaveInstanceState(Bundle outState){
outState.putParcelableArrayList("films", filmParcels);
super.onSaveInstanceState(outState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
// Find GridView to populate with poster images
GridView gridView = (GridView) rootView.findViewById(R.id.gridView);
// Set the adaptor of the GridView to my ImageAdaptor
gridView.setAdapter(mFilmAdaptor);
updateAdaptor();
return rootView;
}
// Update movie data in case there is a change in the "sort by" option
// Or the fragment is started with no saved data
public void updateFilms(){
new FetchFilmTask().execute();
}
public void updateAdaptor(){
mFilmAdaptor.clear();
mFilmAdaptor.addAll(filmParcels);
mFilmAdaptor.notifyDataSetChanged();
}
// Show dialog sort pop up
public void showSortDialog(){
DialogFragment dialog = new SortDialog();
dialog.setTargetFragment(this, 0);
dialog.show(getActivity().getSupportFragmentManager(), "SortDialog");
}
// If a fragment or activity called by this fragment returns to this fragment,
// Get the information returned via the intent
public void onActivityResult(int requestCode, int resultCode, Intent data){
if (requestCode == 0){
int mSelected = data.getIntExtra("Selected Option", -1);
if (mSelected != -1){
sortBy = sortOptions[mSelected];
updateFilms();
updateAdaptor();
}
}
}
// Class to get JSON data from The Movie Database API
public class FetchFilmTask extends AsyncTask<Void, Void, FilmParcelable[]> {
private final String LOG_TAG = FetchFilmTask.class.getSimpleName();
private final String MOVIE_DB_API_KEY = "e1968ef8ba074d7d5bf07a59de8b2310";
protected FilmParcelable[] doInBackground(Void... params){
// These two need to be declared outside the try/catch
// so that they can be closed in the finally block.
HttpURLConnection urlConnection = null;
BufferedReader reader = null;
// Will contain raw JSON response as a string
String movieDBStr = null;
try {
// Construct URL for Movie DB query
Uri.Builder builder = new Uri.Builder();
builder.scheme("http")
.authority("api.themoviedb.org")
.appendPath("3")
.appendPath("discover")
.appendPath("movie")
.appendQueryParameter("api_key", MOVIE_DB_API_KEY)
.appendQueryParameter("sort_by", sortBy);
String myUrl = builder.build().toString();
Log.d(LOG_TAG, myUrl);
URL url = new URL(myUrl);
// Create the request to The Movie DB, and open the connection
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.connect();
// Read the input stream into a String
InputStream inputStream = urlConnection.getInputStream();
StringBuffer buffer = new StringBuffer();
if (inputStream == null){
return null;
}
reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
// Since it's JSON, adding a newline isn't necessary (it won't affect parsing)
// But it does make debugging a *lot* easier if you print out the completed
// buffer for debugging.
buffer.append(line + "\n");
}
if (buffer.length() == 0) {
return null;
}
movieDBStr = buffer.toString();
} catch (IOException e){
Log.e(LOG_TAG, "Error: ", e);
return null;
} finally {
if (urlConnection != null){
urlConnection.disconnect();
}
if (reader != null){
try{
reader.close();
} catch (final IOException e){
Log.e(LOG_TAG, "Error closing stream", e);
}
}
}
try {
return getFilmDataFromJson(movieDBStr);
} catch (JSONException e){
Log.e(LOG_TAG, e.getMessage(), e);
e.printStackTrace();
}
return null;
}
/**
* Take the String representing the complete forecast in JSON Format and
* pull out the data we need to construct the Strings needed for the wireframes.
*
* Fortunately parsing is easy: constructor takes the JSON string and converts it
* into an Object hierarchy for us.
*/
private FilmParcelable[] getFilmDataFromJson(String movieDBStr)
throws JSONException {
// JSON objects that need to be extracted
final String MDB_RESULTS = "results";
final String MDB_ID = "id";
final String MDB_SYNOPSIS = "overview";
final String MDB_RELEASE = "release_date";
final String MDB_POSTER = "poster_path";
final String MDB_TITLE = "title";
final String MDB_RATING = "vote_average";
JSONObject filmJson = new JSONObject(movieDBStr);
JSONArray filmArray = filmJson.getJSONArray(MDB_RESULTS);
FilmParcelable[] resultFilms = new FilmParcelable[filmArray.length()];
for (int i = 0; i < filmArray.length(); i++){
// Data needed by the FilmParcelable
int id;
String title;
String releaseDate;
String posterUrl;
Double voteAverage;
String plotSynopsis;
JSONObject film = filmArray.getJSONObject(i);
id = film.getInt(MDB_ID);
plotSynopsis = film.getString(MDB_SYNOPSIS);
releaseDate = film.getString(MDB_RELEASE);
posterUrl = "http://image.tmdb.org/t/p/w300" + film.getString(MDB_POSTER);
title = film.getString(MDB_TITLE);
voteAverage = film.getDouble(MDB_RATING);
Log.d(LOG_TAG, title);
Log.d(LOG_TAG, posterUrl);
resultFilms[i] = new FilmParcelable(id, title, releaseDate, posterUrl, voteAverage, plotSynopsis);
}
return resultFilms;
}
#Override
protected void onPostExecute(FilmParcelable[] result){
if (result != null){
filmParcels = new ArrayList<>(Arrays.asList(result));
}
}
}
}
ImageAdaptor.java:
public class ImageAdaptor extends ArrayAdapter<FilmParcelable> {
public ImageAdaptor(Activity context, ArrayList<FilmParcelable> filmParcels){
super(context, 0, filmParcels);
}
public View getView(int position, View convertView, ViewGroup parent){
Context context= getContext();
View gridView;
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
String mUrl = getItem(position).getUrl();
if (convertView == null) {
gridView = inflater.inflate(R.layout.gridview_film_layout, parent, false);
// Find the image view from the gridview_film_layout
ImageView posterView = (ImageView) gridView.findViewById(R.id.grid_item_image);
// Set the image view to contain image located at mUrl
Picasso.with(getContext()).load(mUrl).into(posterView);
} else {
gridView = convertView;
}
return gridView;
}
}
accordingly to the code you posted you are returning over and over the same cell of your GridView. You should have those two lines
ImageView posterView = (ImageView) gridView.findViewById(R.id.grid_item_image);
Picasso.with(getContext()).load(mUrl).into(posterView);
out of the if/else guard:
if (convertView == null) {
// inflate
} else {
// gridView = convertView;
}
ImageView posterView = (ImageView) gridView.findViewById(R.id.grid_item_image);
Picasso.with(getContext()).load(mUrl).into(posterView);
return gridView;
I was attempting to update the adapter after populating the arraylist used for the adapter. However the arraylist was populated and updated in the background so the code:
updateFilms();
updateAdaptor();
was causing the adaptor to update before the data had completed loading in the background.
After fixing that Blackbelt's solution was correct.
I've been reading all day threads regarding this issue I came up with a strategy but can't make it work
I have a listview fetching json data from a sql server
this listview already has a on swipe refresh function
I need this listview to refresh automatically only when new row was inserted in the data base.
So I wrote a php file fetching number of rows and echoing it witha 3 second refresh (on the php itself) so every time I enter the php file I get the realtime row numbers of my table.
I'm trying to build a function inside my MainActivity:
int OldNumberOfRows = data from the php file
while(true){
int newNumberOfRows = fetch data again using that php
if(both arent equal) execute refresh command.
}
Note: I got no idea how to extract the string from my asynctask to start manipulating my code with it.
That's it in general, Iv'e added the main activity , the "outer class" (FetchNumRowAsync) calling that php the swipe class and the php itself
MainActivity:
public class MainActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener {
private String TAG = MainActivity.class.getSimpleName();
private String URL = "http://troyka.esy.es/troyka/orders.php";
private SwipeRefreshLayout swipeRefreshLayout;
private ListView listView;
private SwipeListAdapter adapter;
private List<Order> orderList;
// initially offset will be 0, later will be updated while parsing the json
private int offSet = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new FetchRowNumAsync(this).execute("http://troyka.esy.es/numberofrows.php");
listView = (ListView) findViewById(R.id.listView);
//RelativeLayout.LayoutParams layout_description = new RelativeLayout.LayoutParams(50,10);
//Rl.setLayoutParams(layout_description);
swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);
orderList = new ArrayList<>();
adapter = new SwipeListAdapter(this, orderList);
listView.setAdapter(adapter);
swipeRefreshLayout.setOnRefreshListener(this);
/**
* Showing Swipe Refresh animation on activity create
* As animation won't start on onCreate, post runnable is used
*/
swipeRefreshLayout.post(new Runnable() {
#Override
public void run() {
swipeRefreshLayout.setRefreshing(true);
fetchOrders();
}
}
);
}
/**
* This method is called when swipe refresh is pulled down
*/
#Override
public void onRefresh() {
fetchOrders();
}
/**
* Fetching movies json by making http call
*/
private void fetchOrders() {
// showing refresh animation before making http call
swipeRefreshLayout.setRefreshing(true);
// appending offset to url
String url = URL + offSet;
// Volley's json array request object
JsonArrayRequest req = new JsonArrayRequest(url,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
Log.d(TAG, response.toString());
if (response.length() > 0) {
// looping through json and adding to order list
for (int i = 0; i < response.length(); i++) {
try {
JSONObject orderObj = response.getJSONObject(i);
int rank = orderObj.getInt("rank");
String title = orderObj.getString("title");
Order m = new Order(rank, title);
orderList.add(0, m);
// updating offset value to highest value
if (rank >= offSet)
offSet = rank;
} catch (JSONException e) {
Log.e(TAG, "JSON Parsing error: " + e.getMessage());
}
}
adapter.notifyDataSetChanged();
}
// stopping swipe refresh
swipeRefreshLayout.setRefreshing(false);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e(TAG, "Server Error: " + error.getMessage());
Toast.makeText(getApplicationContext(), error.getMessage(), Toast.LENGTH_LONG).show();
// stopping swipe refresh
swipeRefreshLayout.setRefreshing(false);
}
});
// Adding request to request queue
MyApplication.getInstance().addToRequestQueue(req);
}
}
FetchRowNumAsync:
public class FetchRowNumAsync extends AsyncTask<String, Void, String> {
private Context mContext;
public FetchRowNumAsync(Context ctx){
this.mContext = ctx;
}
protected String doInBackground(String... urls)
{
String fullString = "";
try{
URL url = new URL(urls[0]);
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
String line;
while ((line = reader.readLine()) != null) {
fullString += line;
}
reader.close();
}catch(Exception e ){
e.getMessage();
}
return fullString;
}
#Override
protected void onPostExecute(String value){
try{
((OnValueFetchedListener) mContext).onValueFetched(value);
}catch(ClassCastException e){}
}
public interface OnValueFetchedListener{
void onValueFetched(String columns);
}
}
SwipeListAdapter:
public class SwipeListAdapter extends BaseAdapter {
private Activity activity;
private LayoutInflater inflater;
private List<Order> orderList;
private String[] bgColors;
public SwipeListAdapter(Activity activity, List<Order> orderList) {
this.activity = activity;
this.orderList = orderList;
bgColors = activity.getApplicationContext().getResources().getStringArray(R.array.movie_serial_bg);
}
#Override
public int getCount() {
return orderList.size();
}
#Override
public Object getItem(int location) {
return orderList.get(location);
}
#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.list_row, null);
TextView serial = (TextView) convertView.findViewById(R.id.serial);
TextView title = (TextView) convertView.findViewById(R.id.title);
serial.setText(String.valueOf(orderList.get(position).id));
title.setText(orderList.get(position).title);
String color = bgColors[position % bgColors.length];
serial.setBackgroundColor(Color.parseColor(color));
return convertView;
}
}
PHP
<?php
header("refresh: 3;");
$mysqli = new mysqli("irrelevant","irrelevant","irrelevant","irrelevant");
/* check connection */
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
$query = "SELECT COUNT(*) FROM orders";
$result = mysqli_query($mysqli,$query);
$rows = mysqli_fetch_row($result);
echo ($rows[0]);
$result->close();
$mysqli->close();
?>
Try this approach:
Create an endpoint in your server like the following:
//http://somesite.com/api/data/pull/check
Then, you can easily check this endpoint that returns some value like true or false depending on whether there is new data inserted into the db.
From the result you receive, you can then decide on whether to refresh your data on the phone by making another HTTP request or not. You always want to avoid making unnecessary requests to the server - remember users spend money every time they use their data plan (service).
I, like in the comments above, recommend having a column with a timestamp that you can check so that you only get the newly added data instead of everything!
I hope this gives you a simple idea on how to approach this issue! Good luck!
android app will not know when you have added/updated data in your table on the server until and unless you call script from app and fetch the data and update in your device.
only if your app has implemented these feature's
push notification- call Script every time you receive notification.
XMPP service- used for chat apps(which is not probably answer for
your question right now)
here is my suggestion for you
From server side:
create timestamp field in your table on server. update it with
current timestamp value every time you do changes(i.e update/add) in
the table.and when when that script is called send it across in json
and make your app save it in sqlite along with data.
server will compare for timestamp posted by app everytime with the
saved timestamp in the server for new data.
from client side:
for fist time timestamp from app will be 0. server will check it and
send the whole data along with the timestamp saved during changes in
table. save the data along with time stamp . second time when the
script is called App will be sending the timestamp that was last
saved.
with all this your app will not come to know still if new data is added until you call script and check. but atleast it will come to know if new data is received or not and whether to refresh ur screen
now comes script calling part from client side that is executing of assynch task, do it using handler to execute assynch class every minute
final Handler timerHandler = new Handler();
Runnable timerRunnable;
timerRunnable = new Runnable() {
#Override
public void run() {
new FetchRowNumAsync(context).execute(url);
timerHandler.postDelayed(timerRunnable, 60000); // run every minute
}
};
and unregister it in onDestroy()
#Override
public void onDestroyView() {
// TODO Auto-generated method stub
super.onDestroyView();
timerHandler.removeCallbacks(timerRunnable);
}