Sadly my getView method is never called using a custom adapter, I think it has to do something with the Thread.
I need this Thread to not get an Exception because I cant do Network Activity in the Main Thread obviously. Maybe there is a better option for this.
This is my first Project mit API's so I still need to learn a lot! Thanks for your answers!
public class MainActivity extends AppCompatActivity {
List<Schedule> races;
startpage_lv_adapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startPage(2020);
ListView lv = findViewById(R.id.raceList);
lv.setAdapter(adapter);
if(races == null){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
adapter = new startpage_lv_adapter(races, MainActivity.this);
adapter.notifyDataSetChanged();
}
public void startPage(final int year) {
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
Ergast ergast = new Ergast(year, 100, Ergast.DEFAULT_OFFSET);
try {
races = ergast.getSchedules();
} catch (IOException e) {
e.printStackTrace();
}
for (int i = 0; i < races.size(); i++) {
Schedule race = races.get(i);
if (Date.valueOf(race.getDate()).before(Calendar.getInstance().getTime())) {
races.set(i, null);
}
}
races.removeAll(Collections.singletonList(null));
int i = races.size();
if (races.size() == 0) {
startPage(year+1);
}
}
});
thread.start();
}
}
public class startpage_lv_adapter extends BaseAdapter {
private List<Schedule> races;
private Context context;
public startpage_lv_adapter(List<Schedule> races, Context context) {
this.races = races;
this.context = context;
}
#Override
public int getCount() {
return races.size();
}
#Override
public Object getItem(int position) {
return races.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
convertView = LayoutInflater.from(context).inflate(R.layout.listview_events_detail, parent, false);
String basicURL = "https://restcountries.eu/rest/v2/name/";
String country = races.get(position).getCircuit().getLocation().getCountry();
String extendURL = "?fields=alpha2Code";
try {
HttpResponse<String> httpResponse = Unirest.get(basicURL+country+extendURL)
.asString();
country = httpResponse.getBody().split(":")[1].replace('}', ' ').replace('"', ' ').trim();
} catch (UnirestException e) {
e.printStackTrace();
}
ImageView imageView = convertView.findViewById(R.id.img_countryFlag);
TextView countryText = convertView.findViewById(R.id.text_countryName);
TextView roundText = convertView.findViewById(R.id.text_roundNumber);
basicURL = "https://www.countryflags.io/";
extendURL = "/flat/64.png";
Picasso.get().load(basicURL+country+extendURL);
countryText.setText(races.get(position).getCircuit().getLocation().getCountry());
roundText.setText("Round "+ position);
return convertView;
}
}
You have to set adapter after initializing it (set initial value to it) not before, so move this line lv.setAdapter(adapter); after adapter = new startpage_lv_adapter(races, MainActivity.this); , I wonder how your app doesn't crash :D
adapter = new startpage_lv_adapter(races, MainActivity.this);
lv.setAdapter(adapter);
Related
I'm getting the error:
RecyclerView﹕ No adapter attached; skipping layout
I checked some other answers people have given for this issue, but had no luck.
Adapter:
public class MyAdapter extends RecyclerView.Adapter<com.example.myapplication.MyAdapter.MyViewHolder> {
private Context context;
private List<HeadlineModel> headlineModelList;
public MyAdapter(Context context, List<HeadlineModel> headlineModelList) {
this.context = context;
this.headlineModelList = headlineModelList;
}
#NonNull
#Override
public com.example.myapplication.MyAdapter.MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view= LayoutInflater.from(context).inflate(R.layout.news_item_layout,parent,false);
return new MyViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull final com.example.myapplication.MyAdapter.MyViewHolder holder, int position) {
final HeadlineModel headlineModel=headlineModelList.get(position);
holder.newTitle.setText(headlineModel.getTitle());
holder.newsDescription.setText(headlineModel.getDescription());
holder.newsDescription.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Uri uri= Uri.parse(headlineModel.getUrl());
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
context.startActivity(intent);
//Log.v("SSSSSS",headlineModel.getUrl());
}
});
holder.newsName.setText(headlineModel.getName());
holder.newsTime.setText(headlineModel.getPublishedAt());
Glide.with(context).load(headlineModel.getUrlToImage())
.thumbnail(0.5f)
.into(holder.newsImage);
}
#Override
public int getItemCount() {
return headlineModelList.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
TextView newTitle;
TextView newsDescription;
TextView newsName;
TextView newsTime;
ImageView newsImage;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
newTitle=itemView.findViewById(R.id.tv_news_title);
newsDescription=itemView.findViewById(R.id.tv_news_desc);
newsName=itemView.findViewById(R.id.tv_name);
newsTime=itemView.findViewById(R.id.tv_news_date);
newsImage=itemView.findViewById(R.id.im_news_image);
}
}
}
The recycler view in onCreateView in my fragment is initialized like this:
RecyclerView recyclerView;
com.example.myapplication.MyAdapter myAdapter;
private ProgressDialog progressDialog;
List<com.example.myapplication.HeadlineModel> headlineModelList;
private static final String URL="http://newsapi.org/v2/top-headlines?country=in&apiKey=cbd46bd6a4f54fe69d0cb261dbe1a878";
public HeadlinesFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootview=inflater.inflate(R.layout.fragment_headlines, container, false);
// Inflate the layout for this fragment
recyclerView=rootview.findViewById(R.id.headRecyclerView);
headlineModelList=new ArrayList<>();
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL,false));
progressDialog=new ProgressDialog(getContext(),R.style.ProgressColor);
progressDialog.setMessage("loading...");
progressDialog.show();
loadData();
return rootview;
}
loadData() function that is called
private void loadData() {
progressDialog.setMessage("Loading data...");
//progressDialog.show();
JsonObjectRequest jsonObjectRequest=new JsonObjectRequest(Request.Method.GET, URL, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
progressDialog.dismiss();
try {
JSONArray jsonArray=response.getJSONArray("articles");
for (int i=0;i<jsonArray.length();i++){
JSONObject jsonObject=jsonArray.getJSONObject(i);
String title=jsonObject.getString("title");
String desc=jsonObject.getString("description");
String url=jsonObject.getString("url");
String urlToImage=jsonObject.getString("urlToImage");
String publishedAt=jsonObject.getString("publishedAt");
JSONObject source=jsonObject.getJSONObject("source");
String name=source.getString("name");
// for formatting time and date //
String year=publishedAt.substring(0,4);
String month=publishedAt.substring(5,7);
String date=publishedAt.substring(8,10);
String hour=publishedAt.substring(11,13);
//String hour="11";
String min=publishedAt.substring(14,16);
//Log.v("XXXXXX",min);
String updatedDate=date.concat("-").concat(month).concat("-").concat(year).concat(" ");
String print="";
int convertHour= Integer.parseInt(hour);
int convertMin= Integer.parseInt(min);
if (convertHour==12) {
convertHour=12;
print="PM";
}
else if (convertHour>11&&convertMin>0){
convertHour=convertHour-12;
print="PM";
}else {
print="AM";
}
String newHour= String.valueOf(convertHour);
String updatedTime=updatedDate.concat(newHour).concat(":").concat(min).concat(" ").concat(print);
myAdapter=new com.example.myapplication.MyAdapter(getContext(),headlineModelList);
com.example.myapplication.HeadlineModel headlineModel=new com.example.myapplication.HeadlineModel(name,title,desc,url,urlToImage,updatedTime);
headlineModelList.add(headlineModel);
recyclerView.setAdapter(myAdapter);
myAdapter.notifyDataSetChanged();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
})
{
#Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
try {
Cache.Entry cacheEntry = HttpHeaderParser.parseCacheHeaders(response);
if (cacheEntry == null) {
cacheEntry = new Cache.Entry();
}
final long cacheHitButRefreshed = 3 * 60 * 1000; // in 3 minutes cache will be hit, but also refreshed on background
final long cacheExpired = 24 * 60 * 60 * 1000; // in 24 hours this cache entry expires completely
long now = System.currentTimeMillis();
final long softExpire = now + cacheHitButRefreshed;
final long ttl = now + cacheExpired;
cacheEntry.data = response.data;
cacheEntry.softTtl = softExpire;
cacheEntry.ttl = ttl;
String headerValue;
headerValue = response.headers.get("Date");
if (headerValue != null) {
cacheEntry.serverDate = HttpHeaderParser.parseDateAsEpoch(headerValue);
}
headerValue = response.headers.get("Last-Modified");
if (headerValue != null) {
cacheEntry.lastModified = HttpHeaderParser.parseDateAsEpoch(headerValue);
}
cacheEntry.responseHeaders = response.headers;
final String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers));
return Response.success(new JSONObject(jsonString), cacheEntry);
} catch (UnsupportedEncodingException | JSONException e) {
return Response.error(new ParseError(e));
}
}
#Override
protected void deliverResponse(JSONObject response) {
super.deliverResponse(response);
}
#Override
public void deliverError(VolleyError error) {
super.deliverError(error);
}
#Override
protected VolleyError parseNetworkError(VolleyError volleyError) {
return super.parseNetworkError(volleyError);
}
};
RequestQueue queue= Volley.newRequestQueue(getContext());
queue.add(jsonObjectRequest);
}
Does anyone know what I'm doing wrong?
Set the Adapter before the API call success because your API response is async and till will take time depending upon response size, network speed etc, till that time recycler view needs to be created and it is having no adapter.
You can set adaptor before calling loadData() and later when you get the list of response set the data in the adapter of your recylerview and call notify on the adapter.
myAdapter=new com.example.myapplication.MyAdapter(getContext(),headlineModelList);
recyclerView.setAdapter(myAdapter);
use it on onCreateView method and myAdapter.notifyDataSetChanged(); use it when API success
When you click on the CardView, a DialogFragment should appear, but the application crashes, writes the following in the logs
Process: com.example.testfuntura, PID: 15385
java.lang.ClassCastException: android.view.ContextThemeWrapper cannot be cast to androidx.appcompat.app.AppCompatActivity
at com.example.testfuntura.Attraction.AdapterAttractionsPackageOffer.lambda$onBindViewHolder$0(AdapterAttractionsPackageOffer.java:56)
at com.example.testfuntura.Attraction.-$$Lambda$AdapterAttractionsPackageOffer$4cZEQxk2beWA40yZmNkr6ZJp7o8.onClick(lambda)
at android.view.View.performClick(View.java:5692)
at android.view.View$PerformClick.run(View.java:22596)
My Adapter
public class AdapterAttractionsPackageOffer extends RecyclerView.Adapter<AdapterAttractionsPackageOffer.AttractionsViewHolderPackageOffer> {
public ArrayList<ItemPOA> mFavList;
public AdapterAttractionsPackageOffer(ArrayList<ItemPOA> favList) {
mFavList = favList;
}
#NonNull
#Override
public AttractionsViewHolderPackageOffer onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_attraction_package_offer, parent, false);
return new AttractionsViewHolderPackageOffer(v);
}
public static class AttractionsViewHolderPackageOffer extends RecyclerView.ViewHolder {
public ImageView card_image_1;
public TextView Package_offer_title;
public CardView Card;
public AttractionsViewHolderPackageOffer(View itemView) {
super(itemView);
Card = itemView.findViewById(R.id.att_po);
card_image_1 = itemView.findViewById(R.id.img_po);
Package_offer_title = itemView.findViewById(R.id.tex);
}
}
#SuppressLint("LongLogTag")
#Override
public void onBindViewHolder(AttractionsViewHolderPackageOffer holder, int position) {
ItemPOA currentItem = mFavList.get(position);
holder.Package_offer_title.setText(currentItem.get_Package_offer_title());
holder.Card.setOnClickListener(v -> {
DialogPOA dialog = new DialogPOA();
dialog.show(((AppCompatActivity) v.getContext()).getSupportFragmentManager(), "item");
Bundle bundle1 = new Bundle();
bundle1.putString("title_po", currentItem.get_Package_offer_title());
bundle1.putString("description_po", currentItem.get_Package_offer_description());
bundle1.putString("cost_po", currentItem.get_Package_offer_cost());
dialog.setArguments(bundle1);
});
holder.getAdapterPosition();
}
#Override
public int getItemCount() {
return mFavList.size();
}
}
I have a similar Adapter, but when called, everything works, that is, the problem is not in the DialogFragment, since everything works when called from another fragment.
Fragment that the CardView is in
public class FragmentAtt extends Fragment {
ArrayList<ItemPOA> mFavList;
String URL = "http://qqqqqqqqq.mcdir.ru/dbpo.php";
RecyclerView mRec;
RecyclerView.LayoutManager recyclerViewlayoutManager;
RecyclerView.Adapter recyclerViewadapter;
//ImageView GET_PO_IMG;
String GET_PO_TITLE = "title_po";
String GET_PO_DESCRIPTION = "description_po";
String GET_PO_COST = "cost_po";
RequestQueue requestQueue;
JsonArrayRequest jsonArrayRequest;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_test_attraction, container, false);
TabLayout tabLayout = view.findViewById(R.id.tab);
ViewPager viewPager = view.findViewById(R.id.pager);
setupViewPager(viewPager);
tabLayout.setupWithViewPager(viewPager);
return view;
}
#Override
public void onViewCreated(#NonNull final View view, #Nullable Bundle savedInstanceState) {
mRec = requireView().findViewById(R.id.rec_po);
mFavList = new ArrayList<>();
recyclerViewadapter = new AdapterAttractionsPackageOffer(mFavList);
recyclerViewlayoutManager = new LinearLayoutManager(getContext());
mRec.setHasFixedSize(true);
mRec.setLayoutManager(recyclerViewlayoutManager);
mRec.setAdapter(recyclerViewadapter);
JSON_DATA_WEB_CALL2_RINK();
//recyclerViewlayoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false);
//SnapHelper snapHelper = new GravitySnapHelper(Gravity.START);
//snapHelper.attachToRecyclerView(mRec);
}
private void setupViewPager(ViewPager viewPager) {
PagerAdapter adapter = new PagerAdapter(getChildFragmentManager());
adapter.addFragment(new FragmentAttractionRecyclerView(), "Аттракционы");
adapter.addFragment(new FragmentAttractionRecyclerView(), "Видеоигры");
viewPager.setAdapter(adapter);
}
public void JSON_DATA_WEB_CALL2_RINK() {
jsonArrayRequest = new JsonArrayRequest(URL,
this::JSON_PARSE_DATA_AFTER_WEBCALL_RINK,
error ->
Log.e("Volley", error.toString())) {
#Override
protected Response<JSONArray> parseNetworkResponse(NetworkResponse response) {
try {
Cache.Entry cacheEntry = HttpHeaderParser.parseCacheHeaders(response);
if (cacheEntry == null) {
cacheEntry = new Cache.Entry();
}
final long cacheExpired = 24 * 60 * 60 * 1000; // in 24 hours this cache entry expires completely
long now = System.currentTimeMillis();
final long ttl = now + cacheExpired;
cacheEntry.data = response.data;
cacheEntry.softTtl = now;
cacheEntry.ttl = ttl;
String headerValue;
headerValue = response.headers.get("Date");
if (headerValue != null) {
cacheEntry.serverDate = HttpHeaderParser.parseDateAsEpoch(headerValue);
}
headerValue = response.headers.get("Last-Modified");
if (headerValue != null) {
cacheEntry.lastModified = HttpHeaderParser.parseDateAsEpoch(headerValue);
}
cacheEntry.responseHeaders = response.headers;
final String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers));
return Response.success(new JSONArray(jsonString), cacheEntry);
} catch (UnsupportedEncodingException | JSONException e) {
return Response.error(new ParseError(e));
}
}
};
requestQueue = Volley.newRequestQueue(requireContext());
requestQueue.add(jsonArrayRequest);
}
public void JSON_PARSE_DATA_AFTER_WEBCALL_RINK(JSONArray array) {
mFavList.clear();
for (int i = 0; i < array.length(); i++) {
ItemPOA GetDataAdapter2 = new ItemPOA();
JSONObject json;
try {
json = array.getJSONObject(i);
GetDataAdapter2.set_Package_offer_title(json.getString(GET_PO_TITLE));
GetDataAdapter2.set_Package_offer_description(json.getString(GET_PO_DESCRIPTION));
GetDataAdapter2.set_Package_offer_cost(json.getString(GET_PO_COST));
} catch (JSONException e) {
e.printStackTrace();
}
mFavList.add(GetDataAdapter2);
}
recyclerViewadapter = new AdapterAttractionsPackageOffer(mFavList);
mRec.setAdapter(recyclerViewadapter);
}
}
An Adapter that has the same functionality, but why does it work, explain the difference?
public class AdapterSkatingRinkSchedule_2 extends RecyclerView.Adapter<AdapterSkatingRinkSchedule_2.SkatingRinkScheduleViewHolder> {
public ArrayList<ItemSkatingRinkSchedule_2> mSkatingRinkScheduleList;
public AdapterSkatingRinkSchedule_2(ArrayList<ItemSkatingRinkSchedule_2> SkatingRinkScheduleList) {
mSkatingRinkScheduleList = SkatingRinkScheduleList;
}
#NonNull
#Override
public SkatingRinkScheduleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_srs, parent, false);
return new SkatingRinkScheduleViewHolder(v);
}
public static class SkatingRinkScheduleViewHolder extends RecyclerView.ViewHolder {
public TextView number_srs, start_time_srs, end_time_srs, cost_1_srs, cost_2_srs;
public LinearLayout ll_main;
public SkatingRinkScheduleViewHolder(View itemView) {
super(itemView);
ll_main = itemView.findViewById(R.id.ll_main);
number_srs = itemView.findViewById(R.id.number_srs);
start_time_srs = itemView.findViewById(R.id.start_time_srs);
end_time_srs = itemView.findViewById(R.id.end_time_srs);
cost_1_srs = itemView.findViewById(R.id.cost_1_srs);
cost_2_srs = itemView.findViewById(R.id.cost_2_srs);
}
}
#SuppressLint("LongLogTag")
#Override
public void onBindViewHolder(SkatingRinkScheduleViewHolder holder, int position) {
ItemSkatingRinkSchedule_2 currentItem = mSkatingRinkScheduleList.get(position);
holder.number_srs.setText(currentItem.get_session_number());
holder.start_time_srs.setText(currentItem.get_session_start());
holder.end_time_srs.setText(currentItem.get_session_end());
holder.cost_1_srs.setText(currentItem.get_weekdays());
holder.cost_2_srs.setText(currentItem.get_weekends_and_holidays());
holder.ll_main.setOnClickListener(v -> {
DialogSkatingRinkSchedule dialogSRS = new DialogSkatingRinkSchedule();
dialogSRS.show(((AppCompatActivity) v.getContext()).getSupportFragmentManager(), "item");
Bundle bundle2 = new Bundle();
bundle2.putString("number", currentItem.get_session_number());
bundle2.putString("start", currentItem.get_session_start());
bundle2.putString("end", currentItem.get_session_end());
bundle2.putString("w", currentItem.get_weekdays());
bundle2.putString("wah", currentItem.get_weekends_and_holidays());
dialogSRS.setArguments(bundle2);
});
holder.getAdapterPosition();
}
#Override
public int getItemCount() {
return mSkatingRinkScheduleList.size();
}
}
java.lang.ClassCastException: android.view.ContextThemeWrapper cannot be cast to androidx.appcompat.app.AppCompatActivity
Here you are casting a context to an AppCompatActivity but actually Activity is not a Context
You have two options to solve this:
First option: Create a listener interface between the activity/fragment that holds the RecyclerView, implement it in that activity, and trigger its callback within the CardView onClickListener onClick() callback.
Second option: Change the constructor signature of the AdapterAttractionsPackageOffer adapter to accept a FragmentManager instance; save it as a class field like below, and reflect that on the instantiation of the adapter
public class AdapterAttractionsPackageOffer extends RecyclerView.Adapter<AdapterAttractionsPackageOffer.AttractionsViewHolderPackageOffer> {
public ArrayList<ItemPOA> mFavList;
private FragmentManager fragmentMgr;
public AdapterAttractionsPackageOffer(ArrayList<ItemPOA> favList, FragmentManager fManger) {
mFavList = favList;
fragmentMgr = fManger;
}
//......... suppressed code
#SuppressLint("LongLogTag")
#Override
public void onBindViewHolder(AttractionsViewHolderPackageOffer holder, int position) {
ItemPOA currentItem = mFavList.get(position);
holder.Package_offer_title.setText(currentItem.get_Package_offer_title());
holder.Card.setOnClickListener(v -> {
DialogPOA dialog = new DialogPOA();
dialog.show(fragmentMgr, "item");
Bundle bundle1 = new Bundle();
bundle1.putString("title_po", currentItem.get_Package_offer_title());
bundle1.putString("description_po", currentItem.get_Package_offer_description());
bundle1.putString("cost_po", currentItem.get_Package_offer_cost());
dialog.setArguments(bundle1);
});
holder.getAdapterPosition();
}
//......... suppressed code
}
i want create a fragment and have inside a recycler view for show products ...
but when i render it is show this error : RecyclerView: No adapter attached; skipping layout
below i copy my codes;
this code is for adapter class :
private ArrayList<Products> ProductsList;
private Context context;
public Adapter(ArrayList<Products> productsList, Context context) {
ProductsList = productsList;
this.context = context;
}
#Override
public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(context).inflate(R.layout.row_layout, parent, false);
return new MyHolder(v);
}
#Override
public void onBindViewHolder(MyHolder holder, final int position) {
Products products = ProductsList.get(position);
holder.txtName.setText(products.getName());
holder.txtPrice.setText("$ " + products.getPrice());
Picasso.get().load(Config.ipValue + "/images/" + products.getPhoto()).into(holder.imgV);
holder.imgV.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
v.startAnimation(AnimationUtils.loadAnimation(context, android
.R.anim.slide_in_left));
}
});
}
#Override
public int getItemCount() {
return ProductsList.size();
}
class MyHolder extends RecyclerView.ViewHolder {
TextView txtName;
TextView txtPrice;
ImageView imgV;
public MyHolder(View itemView) {
super(itemView);
txtName = itemView.findViewById(R.id.rowTxtProductName);
txtPrice = itemView.findViewById(R.id.rowTxtPrice);
imgV = itemView.findViewById(R.id.rowImgProduct);
}
}
and this one for web api class :
public class WebApiHandler {
Context context;
String apiLink = "";
ArrayList<Products> products = new ArrayList<>();
public WebApiHandler(Context context) {
this.context = context;
}
void apiConnect(String type) {
switch (type) {
case "getproducts": {
apiLink = Config.getProductsWebApi;
break;
}
}
ProgressDialog dialog = ProgressDialog.show(context, "Connecting...",
"please wait", false, false);
StringRequest request = new StringRequest(Request.Method.POST,
apiLink, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
dialog.dismiss();
showJson(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
dialog.dismiss();
}
});
RequestQueue queue = Volley.newRequestQueue(context);
queue.add(request);
}
private void showJson(String response) {
products.clear();
try {
JSONObject jsonObject = new JSONObject(response);
JSONArray jsonArray = jsonObject.getJSONArray("response");
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject object = jsonArray.getJSONObject(i);
String id = object.getString("id");
String name = object.getString("name");
String description = object.getString("description");
String price = object.getString("price");
String photo = object.getString("photo");
Products p = new Products(id, name, description, price, photo);
products.add(p);
}
} catch (JSONException e) {
e.printStackTrace();
}
VerticalFragment.productsArrayList = products;
IData iData = (IData) context;
iData.sendData();
}}
and my fragment code :
public class VerticalFragment extends Fragment implements IData {
RecyclerView rcVertical;
WebApiHandler webApiHandler;
static ArrayList<Products> productsArrayList = new ArrayList<>();
public VerticalFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_vertical, container, false);
rcVertical = view.findViewById(R.id.rcVertical);
webApiHandler = new WebApiHandler(getContext());
webApiHandler.apiConnect("getproducts");
rcVertical.addOnItemTouchListener(new RecyclerTouchListener(getContext(), rcVertical,
new RecyclerTouchListener.ClickListener() {
#Override
public void onClick(View view, int position) {
ProductActivity.products = productsArrayList.get(position);
startActivity(new Intent(getContext(), ProductActivity.class));
}
#Override
public void onLongClick(View view, int position) {
}
}));
return view;
}
#Override
public void sendData() {
Adapter adapter = new Adapter(productsArrayList, getContext());
rcVertical.setLayoutManager(new LinearLayoutManager((getContext())));
rcVertical.setItemAnimator(new DefaultItemAnimator());
rcVertical.setAdapter(adapter);
}}
i should say i create a interface and have one method i say it sendData
This is a common error when there is no adapter attached to recyclerview upon showing recyclerview. It will not cause any harm to your app, but you could avoid it by setting empty adapter without data, and later when you get your data, provide it to your adapter and notify it
Edit - Example added
Create setter for your list in adapter. After that, in your fragment make adapter global and in onCreateView make instance of your adapter and attach it to recyclerview
adapter = new Adapter(new ArrayList<>(), getContext());
rcVertical.setAdapter(adapter);
...Initialize recyclerview...
And then in your sendData interface put your loaded values in it
adapter.setData(productsArrayList);
adapter.notifyDataSetChanged();
I'm currently facing a bug in restoring the Position of an ArrayList after orientation cchange to enhance the user experience. I try to store the ArrayList and already made movieData Parcelable. When using a debugger after orientation change savedInstance obviously is not null and contained an int value and presumably the parcelable Array List and I don't really know why this code doesn't work yet.
Fragment Class:
public class MovieGridFragment extends Fragment {
public clickInterfaceHelper clickListener;
private GridView movieGridView;
private int index;
public List<movieData> movieDataList = new ArrayList<>();
public ArrayList<movieData> restoreList;
public MovieGridFragment() {} //empty constructor
#Override
public void onAttach(Context context) {
super.onAttach(context);
}
public void setClickListener(clickInterfaceHelper listener) {
this.clickListener = listener;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState != null)
index = savedInstanceState.getInt("INDEX");
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
setHasOptionsMenu(true);
if(movieDataList.isEmpty() && networkChecker.isNetworkAvailableChecker(getContext())) {
movieDataList = new ArrayList<movieData>();
}
if(!movieDataList.isEmpty() && !networkChecker.isNetworkAvailableChecker(getContext())) {
movieDataList = new ArrayList<movieData>();
}
View rootView = inflater.inflate(R.layout.movie_display_fragment, container, false);
movieGridView = (GridView) rootView.findViewById(R.id.gv_movie_display);
if(savedInstanceState != null && savedInstanceState.containsKey("OLDMOVIEDATA")) {
//index = savedInstanceState.getInt("INDEX");
//movieDataList.addAll(Arrays.asList((movieData[]) savedInstanceState.getSerializable("OLDMOVIEDATA")));
restoreList = savedInstanceState.getParcelableArrayList("OLDMOVIEDATA");
movieAdapter adapter = new movieAdapter(getActivity(),restoreList);
adapter.notifyDataSetChanged();
//movieGridView.setAdapter(adapter);
movieGridView.smoothScrollToPosition(index);
}
else {
movieAdapter adapter = new movieAdapter(getActivity(), movieDataList);
adapter.notifyDataSetChanged();
movieGridView.setAdapter(adapter);
}
movieGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if(clickListener != null)
clickListener.clickOnItem(position);
}
});
return rootView;
}
#Override
public void onSaveInstanceState(Bundle outState) {
//outState.putSerializable("OLDMOVIEDATA",movieData.movieDataArray);
outState.putParcelableArrayList("OLDMOVIEDATA",restoreList);
outState.putInt("INDEX",movieGridView.getFirstVisiblePosition());
super.onSaveInstanceState(outState);
}
}
Adapter:
public class movieAdapter extends ArrayAdapter<movieData> {
public movieAdapter(Context context, List<movieData> movieObject) {
super(context, 0, movieObject);
}
public View getView(int pos, View convertingView, ViewGroup viewGroup) {
movieData movieDatas = getItem(pos);
String url="http://image.tmdb.org/t/p/w185"+movieDatas.getMovieImagePath();
if(convertingView == null)
convertingView = LayoutInflater.from(getContext()).inflate(R.layout.image_display,viewGroup,false);
ImageView imageView = (ImageView) convertingView.findViewById(R.id.iv_movie_image);
Picasso.with(getContext()).load(url.trim()).into(imageView);
return convertingView;
}
}
and the main activity:
public class MainActivity extends AppCompatActivity implements clickInterfaceHelper {
public static String sorterString = null;
public static String urlBase = "https://api.themoviedb.org/3/movie/";
public static String urlFinal = null;
RequestQueue requestQueue;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState == null) {
MovieGridFragment fragment = new MovieGridFragment();
fragment.setClickListener(this);
getSupportFragmentManager().beginTransaction()
.add(R.id.activity_container, fragment)
.commit();
movieData.movieDataPosition = 0;
}
if(savedInstanceState != null) {
sorterString = savedInstanceState.getString("SORTER");
}
if(sorterString==null)
sorterString="popular?";
if(sorterString!="favorite" && sorterString!=null) {
if(networkChecker.isNetworkAvailableChecker(this)) {
movieRequest();
}
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_menu_act, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if(id == R.id.m_popularity_action) {
if(sorterString != "popular?") {
sorterString = "popular?";
if(networkChecker.isNetworkAvailableChecker(this))
movieRequest();
}
return true;
}
if(id == R.id.m_action_voter) {
if(sorterString != "top_rated?") {
sorterString = "top_rated?";
if(networkChecker.isNetworkAvailableChecker(this))
movieRequest();
}
return true;
}
if(id == R.id.m_favorite_btn) {
if(sorterString != "favorite") {
SQLiteOpenHelper helper = new movieDataDbHelper(this);
SQLiteDatabase database = helper.getReadableDatabase();
Cursor cursor= database.query(movieDataContract.contractEntry.TABLE_NAME,
new String[] {
movieDataContract.contractEntry.ID,
movieDataContract.contractEntry.IMG_PATH},null,null,null,null,null);
if(cursor.getCount() == 0) {
Toast.makeText(this, "there are no favorite movies yet!",Toast.LENGTH_SHORT).show();
} else {
sorterString = "favorite";
showFavoriteFragment();
}
database.close();
helper.close();
cursor.close();
}
return true;
}
return super.onOptionsItemSelected(item);
}
public void showFavoriteFragment() {
favoriteMoviesDetailsFragment fragment = new favoriteMoviesDetailsFragment();
try {
getFragmentManager().beginTransaction().replace(R.id.activity_container,fragment).commit();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
outState.putString("SORTER", sorterString);
outState.putInt("POSITION",movieData.movieDataPosition);
super.onSaveInstanceState(outState, outPersistentState);
}
public void movieRequest() {
final MovieGridFragment gridFragment = new MovieGridFragment();
gridFragment.setClickListener(this);
urlFinal = urlBase + sorterString + movieData.apiKey;
urlFinal.trim();
requestQueue = Volley.newRequestQueue(this);
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, urlFinal, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
JSONArray array = response.getJSONArray("results");
movieData.movieDataArray = new movieData[array.length()];
for (int i = 0; i < array.length(); i++) {
movieData movie = new movieData();
JSONObject jsonObject = array.getJSONObject(i);
movie.setMovieId(jsonObject.getString("id"));
movie.setMovieImagePath(jsonObject.getString("poster_path"));
movie.setMovieTitle(jsonObject.getString("original_title"));
movie.setMoviePlot(jsonObject.getString("overview"));
movie.setMovieVoting(jsonObject.getString("vote_average"));
movie.setMovieReleaseDate(jsonObject.getString("release_date"));
movieData.movieDataArray[i] = movie;
}
gridFragment.movieDataList = Arrays.asList(movieData.movieDataArray);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.activity_container, gridFragment);
try {
transaction.commitAllowingStateLoss();
} catch (Exception e) {
e.printStackTrace();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("volley", String.valueOf(error));
}
}
);
requestQueue.add(jsonObjectRequest);
}
#Override
public void clickOnItem(int id) {
movieData.movieDataPosition = id;
if(movieData.movieDataArray == null) {
movieRequest();
} else {
Intent intent = new Intent(this, detailsActivity.class);
intent.putExtra("FRAGMENT","MOVIE");
startActivity(intent);
}
}
#Override
public void favoriteMovieItem(int movieId) {
movieData.dbPosition = movieId;
Intent intent = new Intent(this,detailsActivity.class);
intent.putExtra("FRAGMENT","favorite");
startActivity(intent);
}
}
You already use the
#Override
public void onSaveInstanceState(Bundle outState) {
//outState.putSerializable("OLDMOVIEDATA",movieData.movieDataArray);
outState.putParcelableArrayList("OLDMOVIEDATA",restoreList);
outState.putInt("INDEX",movieGridView.getFirstVisiblePosition());
super.onSaveInstanceState(outState);
}
Just use it to store the position you need to save. Get the position back using the bundle given in the "onCreateView".
If it does not work, put "setRetainInstance(true)" in the "onCreate" function of your fragment. It will prevent your fragment being destroyed then recreated from nothing during orientation change.
However, I read somewhere that's not the correct way to do so with fragment having UI elements (but i never found anything wrong doing this).
If you want an alternative way not using "setRetainInstance", store (and restore) the position on the activity
public void onSaveInstanceState(Bundle outState)"
function. To do so, create a function "getPosition()" in your fragment, call it in the activity saveInstanceState, and create a function "refresh(int position)" in your fragment (and call it on the restored position value) once the fragment is loaded/recreated in the activity.
I am creating an app where a list of hotels will be shown, all the data is coming from MySQL using JSON and PHP, I created the custom list view by extending the base adapter to a custom one, but I am not able to implement a OnItemClickListener for the listview, as i want to show the Hotel Name of that row in Toast whenever the user clicks on a row of list view. I tried various example available on internet, but i just doesn't work.
Adapter
public class CustomListAdapterHotel extends BaseAdapter {
private Activity activity;
private LayoutInflater inflater;
private List<WorldsBillionaires> billionairesItems;
ImageLoader imageLoader = AppController.getInstance().getImageLoader();
public CustomListAdapterHotel(Activity activity, List<WorldsBillionaires> billionairesItems) {
this.activity = activity;
this.billionairesItems = billionairesItems;
}
#Override
public int getCount() {
return billionairesItems.size();
}
#Override
public Object getItem(int location) {
return billionairesItems.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_hotel, null);
if (imageLoader == null)
imageLoader = AppController.getInstance().getImageLoader();
//NetworkImageView thumbNail = (NetworkImageView) convertView.findViewById(R.id.thumbnail);
TextView hotel_name = (TextView) convertView.findViewById(R.id.hotel_name);
TextView zone = (TextView) convertView.findViewById(R.id.zone);
TextView contact_person = (TextView) convertView.findViewById(R.id.contact_person);
TextView contact_number = (TextView) convertView.findViewById(R.id.contact_number);
TextView btc_direct = (TextView) convertView.findViewById(R.id.btcdirect);
// getting billionaires data for the row
WorldsBillionaires m = billionairesItems.get(position);
// name
hotel_name.setText(String.valueOf(m.getHotel_Name()));
zone.setText(String.valueOf(m.getHotel_Zone()));
contact_person.setText(String.valueOf(m.getContact_Person()));
contact_number.setText(String.valueOf(m.getContact_Number()));
btc_direct.setText(String.valueOf(m.getBtc_Direct()));
return convertView;
}
}
Model
public class WorldsBillionaires {
private String hotel_name,hotel_zone,contact_person,contact_number,btc_direct;
public WorldsBillionaires(String hotel_name, String hotel_zone, String contact_person, String contact_number, String btc_direct) {
this.hotel_name=hotel_name;
this.hotel_zone=hotel_zone;
this.contact_person=contact_person;
this.contact_number=contact_number;
this.btc_direct=btc_direct;
}
public WorldsBillionaires() {
}
public String getZone() {
return zone;
}
public void setZone(String zone) {
this.zone = zone;
}
public String getThumbnailUrl() {
return thumbnailUrl;
}
public void setThumbnailUrl(String thumbnailUrl) {
this.thumbnailUrl = thumbnailUrl;
}
public String getHotel_Name() {
return hotel_name;
}
public void setHotel_Name(String hotel_name) {
this.hotel_name = hotel_name;
}
public String getHotel_Zone() {
return hotel_zone;
}
public void setHotel_Zone(String hotel_zone) {
this.hotel_zone = hotel_zone;
}
public String getContact_Person() {
return contact_person;
}
public void setContact_Person(String contact_person) {
this.contact_person = contact_person;
}
public String getContact_Number() {
return contact_number;
}
public void setContact_Number(String contact_number) {
this.contact_number = contact_number;
}
public String getBtc_Direct() {
return btc_direct;
}
public void setBtc_Direct(String btc_direct) {
this.btc_direct = btc_direct;
}
}
Main Activity
public class ShowHotel extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
// Billionaires json url
private ProgressDialog pDialog;
private List<WorldsBillionaires> worldsBillionairesList = new ArrayList<WorldsBillionaires>();
private ListView listView;
private CustomListAdapterHotel adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_hotel);
getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setLogo(R.mipmap.ic_launcher);
getSupportActionBar().setDisplayUseLogoEnabled(true);
listView = (ListView) findViewById(R.id.list);
adapter = new CustomListAdapterHotel(this, worldsBillionairesList);
listView.setAdapter(adapter);
pDialog = new ProgressDialog(this);
// Showing progress dialog before making http request
pDialog.setMessage("Loading...");
pDialog.show();
// Creating volley request obj
JsonArrayRequest billionaireReq = new JsonArrayRequest("http://192.168.247.1/AdminBihar/getHotel.php?zone="+methods.zone,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
Log.d(TAG, response.toString());
hidePDialog();
// Parsing json
for (int i = 0; i < response.length(); i++) {
try {
JSONObject obj = response.getJSONObject(i);
WorldsBillionaires worldsBillionaires = new WorldsBillionaires();
worldsBillionaires.setHotel_Name(obj.getString("hotel_name"));
worldsBillionaires.setThumbnailUrl(obj.getString("image"));
worldsBillionaires.setHotel_Zone(obj.getString("zone"));
worldsBillionaires.setContact_Person(obj.getString("contact_person"));
worldsBillionaires.setContact_Number(obj.getString("contact_number"));
worldsBillionaires.setBtc_Direct(obj.getString("btc_direct"));
// adding Billionaire to worldsBillionaires array
worldsBillionairesList.add(worldsBillionaires);
} catch (JSONException e) {
e.printStackTrace();
}
}
// notifying list adapter about data changes
// so that it renders the list view with updated data
adapter.notifyDataSetChanged();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
VolleyLog.d(TAG, "Error: " + error.getMessage());
hidePDialog();
}
});
// Adding request to request queue
AppController.getInstance().addToRequestQueue(billionaireReq);
}
#Override
public void onDestroy() {
super.onDestroy();
hidePDialog();
}
private void hidePDialog() {
if (pDialog != null) {
pDialog.dismiss();
pDialog = null;
}
}
}
so after you get json data, in activity that shows your list do something like this:
public class DisplayListView extends AppCompatActivity {
ListView listView;
protected void onCreate(){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_display_list_view);
listView = (ListView) findViewById(R.id.listview);
hotelAdapter = new CustomListAdapterHotel (this, R.layout.row_layout);
listView.setAdapter(hotelAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id){
Model stuff = (Model) hotelAdapter.getItem(position);
String hotelName = stuff.getHotel_Name();
Toast.makeText(getApplicationContext(), hotelName, Toast.LENGTH_SHORT).show();
}
});
there, this worked for me :)