I am working through John Horton's Android Programming for Beginners, and am currently attempting to create a note-taking app. Horton has just introduced ListViews. However, I am having trouble with the adapter class:
public class NoteAdapter extends BaseAdapter {
List<Note> mNoteList = new ArrayList<Note>();
#Override
public int getCount(){
return mNoteList.size();
}
#Override
public Note getItem(int whichItem){
return mNoteList.get(whichItem);
}
#Override
public long getItemId(int whichItem){
return whichItem;
}
#Override
public View getView(int whichItem, View view, ViewGroup viewGroup){
// check if view has been inflated already
if (view == null){
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); // ERROR HERE
view = inflater.inflate(R.layout.listitem, viewGroup, false);
}
return view;
}
}
The problem is in the getView method, where I'm attempting to inflate the layout: Android Studio throws an error: 'Cannot resolve getSystemService(java.lang.String)'.
As a complete newcomer just following through the book I have no idea where to go from here or what to try to resolve it - can anyone help?
The best way to get a LayoutInflater is by calling getLayoutInflater() on an Activity. That way, the activity's theme is taken into account. If NoteAdapter is defined inside of an Activity, just call getLayoutInflater(). If NoteAdapter is defined in its own separate Java class file, pass in a LayoutInflater via the constructor.
To more directly address your question, any View, like ListView, can call getContext() to get a Context. That is where getSystemService() is defined. So, replacing getSystemService() with viewGroup.getContext().getSystemService() would work.
You should pass Context to your adapter and then replace this line:
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
I hope this will help.
Use
view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.listitem, viewGroup,false);
Create a class variable and a Constructor for your adapter:
Context context;
public NoteAdapter(Context context){
this.context = context;
}
Then initialize the layoutinflater the following way:
LayoutInflater inflater = LayoutInflater.from(context);
Try
public class NoteAdapter extends BaseAdapter {
Context mContext = null;
public NoteAdapter(Context context){
mContext = context;
}
#Override
public View getView(int whichItem, View view, ViewGroup viewGroup){
// check if view has been inflated already
if (view == null){
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); // ERROR HERE
view = inflater.inflate(R.layout.listitem, viewGroup, false);
}
return view;
}
}
First make the constructor of Adapter: like follow :
Context context;
public NoteAdapter(Context context)
{
this.context = context
}
Now use this context:
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
In my views, if you are learning then learn RecyclerView. bcz it is better than ListView. i am not saying that ListView has been depricated. But there alot of internal things in which RecyclerView is better.
Following is example of Adapter
public class NoteAdapter extends BaseAdapter {
List<Note> mNoteList = new ArrayList<Note>();
Context context;
public NoteAdapter(Context context){
this.context = context;
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount(){
return mNoteList.size();
}
#Override
public Note getItem(int whichItem){
return mNoteList.get(whichItem);
}
#Override
public long getItemId(int whichItem){
return whichItem;
}
#Override
public View getView(int whichItem, View view, ViewGroup viewGroup){
// check if view has been inflated already
if (view == null){
view = inflater.inflate(R.layout.listitem, viewGroup, false);
}
return view;
}
}
Inside MainActivity.java
NoteAdapter noteA = new NoteAdapter(MainActivity.this);
OR
NoteAdapter noteA = new NoteAdapter(getContext());
OR
NoteAdapter noteA = new NoteAdapter(getActivity);
// if in Fragment
OR
NoteAdapter noteA = new NoteAdapter(getApplicationContext);
// will work but no need to use it. bcz this is context of whole application. For an adapter you don't need context of whole application.
mContext is Context which you pass to Custom Adapter
public boolean CheckInternet() {
ConnectivityManager connectivityManager = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState() == NetworkInfo.State.CONNECTED ||
connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState() == NetworkInfo.State.CONNECTED) {
//we are connected to a network
return true;
}
return false;
}//end of check internet
Related
i want know why getview() function is not call
maybe override is wrong?
i want make custom listlayout
what's wrong in listviewadapter?
public class ListViewAdapter extends BaseAdapter {
private Context mContext;
private ArrayList<Word> items;
public ListViewAdapter(Context mContext, ArrayList<Word> items) {
this.mContext = mContext;
this.items = items;
}
#Override
public int getCount() {
return items.size();
}
#Override
public Object getItem(int i) {
return items.get(i);
}
#Override
public long getItemId(int i) {
return i;
}
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
if (view == null) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.wordlist_layout, viewGroup, false);
}
TextView wordview1 = view.findViewById(R.id.wordView1);
System.out.println("wordlist layout = " + items.get(i).w1);
wordview1.setText(items.get(i).w1);
return view;
}
// this is my Main activity code
private ArrayList<Word> items;
private ListViewAdapter listviewadapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn = findViewById(R.id.submit_Button);
btn.setOnClickListener(view -> {
ListView word_listview = findViewById(R.id.Wordle_Word_List);
items = new ArrayList<Word>();
items.add(new Word('a'));
listviewadapter = new ListViewAdapter(getApplicationContext(), items);
word_listview.setAdapter(listviewadapter);
});
}
i dont know what is missing in my code
In ListviewAdapter I dont know why getview() function not call
I think your problem is the condition in its body, not the whole function. There is no need to check if it is null or not. you can simply inflate the view as mentioned in this article:
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.wordlist_layout, viewGroup, false);
TextView wordview1 = view.findViewById(R.id.wordView1);
System.out.println("wordlist layout = " + items.get(i).w1);
wordview1.setText(items.get(i).w1);
return view;
}
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
view = LayoutInflater.from(mContext).inflate(R.layout.wordlist_layout, viewGroup, false);
TextView wordview1 = view.findViewById(R.id.wordView1);
wordview1.setText(items.get(i).w1);
return view;
}
Super hard one to explain.
This is the error I get in my reporting:
Attempt to invoke virtual method 'java.lang.Object android.content.Context.getSystemService(java.lang.String)' on a null object reference
This seems to happen intermittently when you go into and out of the fragment. The error seems to happen in the adaptor.
This is where it is called:
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
getActivity().setTitle("Shipments");
myView = inflater.inflate(R.layout.shipments_out_layout, container, false);
listView = myView.findViewById(R.id.listView);
fetchShipments();
return myView;
}
/**
* Fetch shipments
*/
public void fetchShipments()
{
shipmentsService.fetchFromServer(getActivity());
}
/**
* Show shipments
*/
public void showShipments(){
RealmResults<Shipment> savedShipments = shipmentsService.all();
ShipmentsAdaptor adaptor = new ShipmentsAdaptor(savedShipments, this.getContext());
listView.setAdapter(adaptor);
}
And this is where the error is in the adaptor:
public class ShipmentsAdaptor extends ArrayAdapter<Shipment> {
private RealmResults<Shipment> dataSet;
Context mContext;
// View lookup cache
private static class ViewHolder {
TextView stockItemId;
TextView technicianName;
TextView shipmentDate;
}
public ShipmentsAdaptor(RealmResults<Shipment> data, Context context){
super(context, R.layout.shipments_out_row_item, data);
this.dataSet = data;
this.mContext = context;
}
It's this line specifically: super(context, R.layout.shipments_out_row_item, data);
I thought it may be something to do with the way we are inserting the context into the adaptor and then changing the page before its finished but that proved inconclusive.
Paste bin with adaptor:Adaptor
The Fragment#getContext() is nullable. This method returns null when your fragment is detached from activity. The app crashes because you create the adapter while the fragment is not attached which results into a null passed to the constructor.
The method showShipments should only be called when the fragment is attached to the activity. There are callbacks onAttach() and onDetach() that will help you to detect the state. Also isAdded() returns you a boolean saying if the fragment is attached or not. Choose what is convenient for you.
Good luck!
Try refactor your adapter using BaseAdapter as follow
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
public class ShipmentsAdaptor extends BaseAdapter {
private RealmResults<Shipment> dataSet;
private Context mContext;
// View lookup cache
private static class ViewHolder {
TextView stockItemId;
TextView technicianName;
TextView shipmentDate;
}
public ShipmentsAdaptor(RealmResults<Shipment> dataSet, Context context) {
this.dataSet = dataSet;
this.mContext = context;
}
#Override
public int getCount() {
return dataSet.size();
}
#Override
public Object getItem(int position) {
return dataSet.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
Shipment shipment = (Shipment) getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
ViewHolder viewHolder; // view lookup cache stored in tag
final View result;
if (convertView == null) {
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(mContext);
convertView = inflater.inflate(R.layout.shipments_out_row_item, parent, false);
viewHolder.stockItemId = convertView.findViewById(R.id.stockItemId);
viewHolder.technicianName = convertView.findViewById(R.id.technicianName);
viewHolder.shipmentDate = convertView.findViewById(R.id.shipmentDate);
result = convertView;
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
result=convertView;
}
lastPosition = position; //use getItemId() instead
if(shipment != null){
viewHolder.stockItemId.setText(String.valueOf(shipment.id));
if(shipment.technician != null){
viewHolder.technicianName.setText(shipment.technician.name);
}
viewHolder.shipmentDate.setText(shipment.shippingDate);
}
// Return the completed view to render on screen
return convertView;
}
}
Looks like you are calling fetchShipments(); before the fragment layout view (myView) is returned hence it is null when the adaptor is instantiated.
Try:
Move fetchShipments(); from onCreateView() and place it in onResume() or override onStart() and call it from there
You can check for null during your adapter setup to avoid this. In a Fragment, getActivity can sometimes return null at different points during the Fragment lifecycle. For example, in showShipments
Activity a = getActivity();
if( a == null || a.isFinishing() ) {
// Not in a valid state to show things anyway, so just stop and exit
return;
}
ShipmentsAdaptor adaptor = new ShipmentsAdaptor(savedShipments, a);
You can also check isAdded(), and if that is false you can get null from getActivity().
Also, consider moving the call to fetchShipments() from onCreateView to onActivityCreated instead.
My application gets all images URL from server and saves that to an ArrayList and displays these images in ViewPager. But it generates a IllegalStateException. Adapter given below:
public class FullScreenImageAdapter extends PagerAdapter {
private Context _activity;
private ArrayList<String> _imagePaths;
private LayoutInflater inflater;
// constructor
public FullScreenImageAdapter(Context activity,
ArrayList<String> imagePaths) {
this._activity = activity;
this._imagePaths = imagePaths;
}
#Override
public int getCount() {
return this._imagePaths.size();
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
ImageView imgDisplay;
inflater = (LayoutInflater) _activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View viewLayout = inflater.inflate(R.layout.item, container,
false);
imgDisplay = (ImageView) viewLayout.findViewById(R.id.cardImage);
Picasso.with(_activity).load(_imagePaths.get(position)).into(imgDisplay);
((ViewPager) container).addView(viewLayout, 0);
return viewLayout;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
((ViewPager) container).removeView((ImageView) object);
}
}
Adapter created as
FullScreenImageAdapter adapter=new FullScreenImageAdapter(FullScreenActivity.this,all_url);
viewPager.setAdapter(adapter);
And the log looks like below:
java.lang.IllegalStateException: The application's PagerAdapter
> changed the adapter's contents without calling
> PagerAdapter#notifyDataSetChanged! Expected adapter item count: 1,
> found: 3 Pager id: com.wat.clickzy:id/view_pager Pager class: class
> android.support.v4.view.ViewPager Problematic adapter: class
> com.wat.clickzy.FullScreenImageAdapter
Please help me
You need to call notifysetdatachanged on the adapter that you're using, every time you're adding/removing something to that adapter.
Look here for even more clarity.
Im trying to add a listview inside a fragment witch is also inside a tabhost. the app is not crashing or anything, but the list view is not showing anything.
Here is my code:
Class for the tabPager
public class TabPagerAdapter extends FragmentPagerAdapter {
public TabPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int index) {
switch (index) {
case 0:
return new FragmentHome();
case 1:
return new FragmentBadges();
case 2:
return new FragmentBenefits();
}
return null;
}
Following is the fragment where I want the listview:
public class FragmentBadges extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_badges_list, null);
BadgesListAdapter adapter = new BadgesListAdapter(this.getActivity(), getCurrentBadges());
ListView badgeslist = (ListView) rootView.findViewById(R.id.badges_list);
badgeslist.setAdapter(adapter);
badgeslist.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
//TODO do some
}
});
return rootView;
}
And following is the adapter:
public class BadgesListAdapter extends ArrayAdapter<String> {
private final Activity context;
private List<Badge> badges;
public BadgesListAdapter(Activity context, List<Badge> badges) {
super(context, R.layout.fragment_badges_listrow);
this.context = context;
this.badges = badges;
}
#Override
public View getView(int position, View view, ViewGroup parent) {
LayoutInflater inflater = context.getLayoutInflater();
View rowView= inflater.inflate(R.layout.fragment_badges_listrow, null, true);
TextView txtTitle = (TextView) rowView.findViewById(R.id.text);
ImageView imageView = (ImageView) rowView.findViewById(R.id.img);
txtTitle.setText(badges.get(position).getName());
imageView.setImageResource(badges.get(position).getDrawableValue());
return rowView;
}
while debugging I found out that the getView method inside the badgesListAdapter is never called.
I have this same code for another app but without the tabpager and there it all works perfectly...
Check whether getCurrentBadges() method not returns empty list. If so there is no data to show in ListView.
I was able to make it work by inhering ListFragment in FragmentBadges, following this tutorial https://www.youtube.com/watch?v=heR4zhj_wV0
I am making an Custom listview with Custom Adapter extending BaseAdapter. Now I want to set a different layout only for first row and another layout for all other rows. I am using this code but it sets the special layout for 1st row and also repeats it after every 5/6 rows. How can I fix it and can set it only for 1st row and another layout for all other rows.
public class NewsAdapter extends BaseAdapter {
private Context context;
List<News> newsList;
private Typeface customBanglaFont;
private LayoutInflater inflater;
ImageLoader imageLoader = AppController.getInstance().getImageLoader();
public NewsAdapter(Context context, List<News> newsList){
this.context = context;
this.newsList = newsList;
}
#Override
public int getCount() {
return newsList.size();
}
#Override
public Object getItem(int arg0) {
return newsList.get(arg0);
}
#Override
public long getItemId(int arg0) {
return arg0;
}
#Override
public View getView(int position, View convertView, ViewGroup viewGroup) {
View row;
row = convertView;
ViewHolder holder=null;
if(row == null){
if(position == 0){
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row =inflater.inflate(R.layout.row_single_big_view,viewGroup,false);
}
else{
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row =inflater.inflate(R.layout.row_single_default,viewGroup,false);
}
holder = new ViewHolder(row);
row.setTag(holder);
}
else{
holder = (ViewHolder) row.getTag();
}
if (imageLoader == null){
imageLoader = AppController.getInstance().getImageLoader();
}
final News news = newsList.get(position);
customBanglaFont = Typeface.createFromAsset(viewGroup.getContext().getAssets(), "fonts/SolaimanLipi.ttf");
holder.newsTitleView.setTypeface(customBanglaFont);
holder.newsTitleView.setText(news.getTitle());
holder.thumbNail.setImageUrl(news.getFeaturedImgSrc(), imageLoader);
return row;
}
}
/**
* Custom View Holder Class
* #author Tonmoy
*
*/
class ViewHolder{
TextView newsTitleView;
NetworkImageView thumbNail;
public ViewHolder(View v) {
// TODO Auto-generated constructor stub
newsTitleView = (TextView) v.findViewById(R.id.news_title);
thumbNail = (NetworkImageView) v.findViewById(R.id.news_image);
}
}
Why don't you use the Header functionality already built into ListView for this specific thing.
The docs:
http://developer.android.com/reference/android/widget/ListView.html#addHeaderView%28android.view.View,%20java.lang.Object,%20boolean%29
or a SO question:
Using ListView : How to add a header view?
your attempting is wrong. Your way you will be able to inflate just one layout. In fact, after the method returns the first time, convertView is not null. To fix quickly you could override getViewTypeCount and getItemViewType. This way you will get convertViews eqaul to the return value of getViewTypeCount, or you could just add the first item as header view for the ListView