Using enums to pass data to listviews - java

I'm attempting to use multiple enums that I've set as subclasses to a master class as the data source for listviews on separate fragments.
Each fragment will have two listviews, but each listview will only need to use certain parts of the data. Each enum has two strings "name" and "abbr" and a double "value".
I would like to set both strings as the ListView titles, and use the value in a calculation.
Listview1 will hold titles, abbrs, and one EditText in the center row. Listview2 will hold titles, abbrs, and one more TextView that will update based on the EditText input and the value from the enum. I realize I will need two custom adapters for this, one for the heterogeneous Listview1, and one for Listview2.
I am a little lost on implementing, and have only attempted doing the custom adapter for Listview2.
I have tried looking at multiple SO questions, and listview tutorials that use database models and then have tried to use that but with my static enum lists, but am just a bit lost. Any help from a high level approach, specifics, or a nice tutorial would be much appreciated. I am probably not even close on the right path as I am new at Android and relatively new at OOP, thanks for bearing with the poor code!
What I have so far (I set up a test project, which is why I have this listview inflated in main as opposed to in a fragment - if there are any issues with this besides switching the context let me know):
Enum class holding all enums
public class Enums {
public enum Pressures{
ITEM1 ("name", "abbr", 1.0),
etc...;
private final String name;
private final String abbr;
private final double value;
Pressures(String name, String abbr, double value) {
this.name = name;
this.abbr = abbr;
this.intermediary = intermediary;
}
public String getNames() {
return name;
}
public String getAbbr() {
return abbr;
}
public double getIntermediary() {
return intermediary;
}
}
public enum Enum2 {
...
}
}
Custom Adapter for Listview2:
public class CustomAdapter extends BaseAdapter {
private Activity activity;
private LayoutInflater inflater;
private List<Enums.Pressures> pressureEnum;
public CustomAdapter(Activity activity, List<Enums.Pressures> units) {
this.activity = activity;
this.pressureEnum = units;
}
#Override
public int getCount() {
return pressureEnum.size();
}
#Override
public Object getItem(int location) {
return pressureEnum.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 name = (TextView) convertView.findViewById(R.id.name);
TextView abbr = (TextView) convertView.findViewById(R.id.abbr);
Enums.Pressures p = pressureEnum.get(position);
name.setText(p.getNames());
abbr.setText(p.getAbbr());
return convertView;
}
}
Main:
public class Main extends Activity {
//This List was a poor attempt at setting the list from the enum
//I don't believe ArrayList is the proper choice as I have an enum object
//but I'm not quite sure what to use
private List<Enums.Pressures> pressureUnits = new ArrayList<Enums.Pressures>();
private ListView listView;
private CustomAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView) findViewById(R.id.list);
adapter = new CustomAdapter(this, pressureUnits);
listView.setAdapter(adapter);
}
}

Your List "pressureUnits" is empty. You've specified that it is a list of Pressures but not added anything to it. Usually this is done with the "add" method, i.e. pressureUnits.add(...).
However what I think you want to use is Enums.Pressures.values(). This will return an array containing each of your enum elements. Then you will be able to create an adapter using that array. If you can't do it with the BaseAdapter you are using now, have a look at using the ArrayAdapter class rather than the BaseAdapter.

Related

Remove certain items which are empty from RecyclerView list?

I'm making an app using TMDB API and have gotten stuck at a small issue.
TMDB API shows seasons and episodes which are empty, basically, those are yet to air but since those are empty, the app shows a blank item that I'm trying to get rid of.
Here's my adapter:
public class SeasonAdapter extends RecyclerView.Adapter<SeasonAdapter.ViewHolder> {
private final List<Season> seasons;
private final Context context;
private final RequestOptions requestOptions;
public SeasonAdapter(List<Season> seasons, Context context) {
this.seasons = seasons;
this.context = context;
requestOptions = new RequestOptions().centerCrop().placeholder(R.drawable.poster_placeholder).error(R.drawable.poster_placeholder);
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_season_item, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
final Season season = seasons.get(position);
holder.tvTitle.setText(season.getSeasonTitle());
if (season.getSeasonDate() != null && !season.getSeasonDate().isEmpty()) {
holder.tvDate.setText(context.getResources().getString(R.string.aired_on) + season.getSeasonDate());
} else {
holder.tvDate.setVisibility(View.GONE);
}
if (season.getSeasonEpisodes() == 0) {
seasons.remove(position);
}
holder.tvEpisodes.setText(String.valueOf(season.getSeasonEpisodes()) + context.getResources().getString(R.string.total_episodes));
Glide.with(context).load(season.getSeasonImageURL()).apply(requestOptions).into(holder.ivPoster);
holder.itemView.setOnClickListener(v -> {
Intent intent = new Intent(context, EpisodeActivity.class);
intent.putExtra("title", season.getShowTitle());
intent.putExtra("seasonTitle", season.getSeasonTitle());
intent.putExtra("seasonNo", season.getSeasonNo());
intent.putExtra("tvId", season.getTvId());
v.getContext().startActivity(intent);
});
}
#Override
public int getItemCount() {
return seasons.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public ImageView ivPoster;
public TextView tvTitle, tvDate, tvEpisodes;
public ViewHolder(#NonNull View itemView) {
super(itemView);
ivPoster = itemView.findViewById(R.id.ivSeasonPoster);
tvTitle = itemView.findViewById(R.id.tvSeasonTitle);
tvDate = itemView.findViewById(R.id.tvSeasonAired);
tvEpisodes = itemView.findViewById(R.id.tvSeasonEpisodes);
//Poster Corners
ivPoster.setClipToOutline(true);
}
}
}
I tried doing this:
if (season.getSeasonEpisodes() == 0) {
seasons.remove(position);
}
It does seem to hide the season which has no episodes but if a show has multiple seasons without episodes, my app crashes so I figured this isn't the right solution so any help is appreciated.
I suggest performing that removal logic in the constructor of the adapter rather than in onBind. onBind happens as the recycler view is finalising the details of each view holder immediately before it's shown to the user. You want to do as little as possible logic in here to keep the recycler view performant.
Inside the constructor (or even before the list is passed in) you should perform a loop and remove those items that don't meet the criteria before setting the instance variable.
It's been a long time since I wrote code in java and so I'd end up with unhelpful incorrect syntax if I tried to do it here.

Reuse the RecyclerView adapter

I have very small question here, in my recycle view adapter class i'm using List<FeaturedTags> and its working fine.
Now we have newly introduced class called 'FeaturedLangTags, the only difference between FeaturedTags & FeaturedLangTagsis just an addition of Lang field. But we are not using this Lang field anyway to show on screen.
The output of the recycle view looks exactly similar to existing FeaturedTags adapter. Here i want to know how i can re-use the existing adapter class to display List<FeaturedLangTags> items?
One simple way is to duplicate the existing adapter and pass the FeaturedLangTagslist, but here so much of code is getting duplicated. I would like to know how i can tweek the existing class?
Create Adapter of generic List<T> which can be used in both condition.
public abstract class GenericAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context context;
private ArrayList<T> items;
public abstract RecyclerView.ViewHolder setViewHolder(ViewGroup parent);
public abstract void onBindData(RecyclerView.ViewHolder holder, T val);
public GenericAdapter(Context context, ArrayList<T> items){
this.context = context;
this.items = items;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder holder = setViewHolder(parent);
return holder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
onBindData(holder,items.get(position));
}
#Override
public int getItemCount() {
return items.size();
}
public void addItems( ArrayList<T> savedCardItemz){
items = savedCardItemz;
this.notifyDataSetChanged();
}
public T getItem(int position){
return items.get(position);
}
}
adapter = new GenericAdapter<DataModel>(context,modelList) {
#Override
public RecyclerView.ViewHolder setViewHolder(ViewGroup parent) {
final View view = LayoutInflater.from(context).inflate(R.layout.item_view_holder, parent, false);
ItemViewHolder viewHolder = new ItemViewHolder(context, view);
return viewHolder;
}
#Override
public void onBindData(RecyclerView.ViewHolder holder1, DataModel val) {
DataModel userModel = val;
ItemViewHolder holder = (ItemViewHolder)holder1;
holder.name.setText(userModel.getName());
holder.fatherName.setText(userModel.getFatherName());
}
};
mRecyclerView.setAdapter(adapter);
Create a common interface or superclass to FeaturedTags and FeaturedLangTags, then use the same adapter.
Or use a more generic approach to your adapter like AdapterDelegates.
You can create two different constructors for the adapter and achieve it.
You can have both the classes implement one common interface say CommonInterface
Make your list of type
YourList<CommonInterface>
In your adapter, override the
getItemViewType
Method. Here use the instance of operator to check what type of class it is and return a view type integer. Say for without lang, it will be an integer 0 and with it, 1
Now in your onCreateViewHolder, you get passed the viewType as the second parameter. Using that inflate your layout as your choice for the view.
In your onBindViewHolder, you can check
getItemViewType
On the holder passed in the parameter and cast your interface explicitly to the object you want. Then use it as usual.
Reference for this multiple view types is
Recycler view multiple view types
You can use ConcatAdapter of RecyclerView to concatenate your adapters and reuse them.
MyAdapter adapter1 = ...;
AnotherAdapter adapter2 = ...;
ConcatAdapter concatenated = new ConcatAdapter(adapter1, adapter2);
recyclerView.setAdapter(concatenated);
"Favor composition over inheritance"

getItem(int) clashes with getItem(int)

I'm new to Java, so please excuse me if this is a dumb question.
I got this class:
public class CustomDrawerAdapter extends ArrayAdapter<DrawerItem> {
Context context;
List<DrawerItem> drawerItemList;
int layoutResID;
private FriendInfo[] friends = null;
public void setFriendList(FriendInfo[] friends)
{
this.friends = friends;
}
public int getCount() {
return friends.length;
}
public FriendInfo getItem(int position) {
return friends[position];
}
public long getItemId(int position) {
return 0;
}
public CustomDrawerAdapter(Context context, int layoutResourceID,
List<DrawerItem> listItems) {
super(context, layoutResourceID, listItems);
this.context = context;
this.drawerItemList = listItems;
this.layoutResID = layoutResourceID;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
DrawerItemHolder drawerHolder;
View view = convertView;
if (view == null) {
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
drawerHolder = new DrawerItemHolder();
view = inflater.inflate(layoutResID, parent, false);
drawerHolder.ItemName = (TextView) view
.findViewById(R.id.drawer_itemName);
drawerHolder.icon = (ImageView) view.findViewById(R.id.drawer_icon);
view.setTag(drawerHolder);
} else {
drawerHolder = (DrawerItemHolder) view.getTag();
}
DrawerItem dItem = (DrawerItem) this.drawerItemList.get(position);
drawerHolder.icon.setImageDrawable(view.getResources().getDrawable(
dItem.getImgResID()));
drawerHolder.ItemName.setText(friends[position].userName);
return view;
}
private static class DrawerItemHolder {
TextView ItemName;
ImageView icon;
}
At FriendInfo I get the following error:" 'getItem(int)' in '(My Package name).CustomDrawerAdapter' clashes with 'getItem(int)' in 'android.widget.ArrayAdapter'; attempting to use incompatible return type"
I'm new to Java and I don't know how to fix this, could somebody help me?
Your CustomDrawerAdapter extends ArrayAdapter<T> with T being a DrawerItem. T is a type parameter which is replaced by a type argument when you define your CustomDrawerAdapter class, the type argument being DrawerItem in your case.
So CustomDrawerAdapter is basically an adapter pulling the items to show from an array of DrawerItems.
The ArrayAdapter defines a method
public T getItem (int position)
which will be
public DrawerItem getItem (int position)
for your class CustomDrawerAdapter.
You might want to read this tutorial about generics: http://docs.oracle.com/javase/tutorial/java/generics. Note that generics are not easy to understand but you absolutely need a basic understanding when doing Android development.
If you decide to override that method then you can't change the return value unless the method has a different signature (name, plus the number and the type of its parameters).
You have to ask yourself: is CustomDrawerAdapter an adapter pulling its data from an array (or list) of FriendInfo or from an array of DrawerItem? Depending on the answer your class definition would be
public class CustomDrawerAdapter extends ArrayAdapter<DrawerItem>
or
public class CustomDrawerAdapter extends ArrayAdapter<FriendInfo>
Using both classes to back the ArrayAdapter doesn't make sense unless FriendInfo is extending DrawerItem and that seems unlikely.
If you really need a second array of Objects (FriendInfo) for whatever purposes, then don't modify the ArrayAdapter methods (like getCount(), getItemId(int) or getItem(int)) unless you know exactly what you're doing.
E.g. your getCount() is doomed to fail because it returns the size of FriendInfo[] instead of the number of DrawerItems and FriendInfo[] might not have the same number of elements (it certainly doesn't have before you call the setFriendList and who guarantees that the passed array has the same number of elements as the List you pass in the constructor?).
Because it extends ArrayAdapter<DrawerItem>, getItem needs to return a DrawerItem. If you can't find a clean way to fix this while extending ArrayAdapter, it's pretty easy to make your own adapter implementation by extending BaseAdapter.

Unable to access variable inside listener

I have a listener excerpt below which contains the variable degree.
public class CustomOnItemSelectedListener implements AdapterView.OnItemSelectedListener {
//Define Components
public EditText text;
public Spinner spinner1;
//Define Variables
public String degree;
public void onItemSelected(AdapterView<?> parent, View view, int pos,long id) {
//Set the selected item on spinner1 to the variable tempValue
String tempValue = spinner1.getSelectedItem().toString();
degree = "jobby";
}
I am trying to access that variable within my ViewPager adapter like so:
public class MyPagerAdapter extends FragmentPagerAdapter {
public CustomOnItemSelectedListener selectedListener;
[...]
#Override
public Fragment getItem(int pos) {
switch(pos) {
case 0: return resultFragment.newInstance(selectedListener.degree);
case 1: return resultFragment.newInstance("resultFragment, Instance 2");
default: return resultFragment.newInstance("resultFragment, Default");
}
}
[...]
}
And in my MainActivity
protected void onCreate(Bundle savedInstanceState) {
[...]
myPager = new MyPagerAdapter(getSupportFragmentManager());
selectedListener = new CustomOnItemSelectedListener();
addListenerOnSpinnerItemSelection();
myPager.selectedListener = selectedListener;
}
The issue I am having is that I am unable to access the variable of degree. If I set the value of the variable at the top of the listener class like so
public String degree = "jobby";
Then I can access the variable.
I am trying to write an if statement within the listener, and depending on which item of the spinner is selected, the variable changes.
How can I access the variable within the listener class?
I think it's because you need initialize the variable, try this:
public String degree = null;
And make sure when you use the variable it's with the correct value, to try avoid NullPointerExceptions.
In CustomOnItemSelectedListener#onItemSelected the fragment needs to be replaced using the Activity's FragmentManager.
Refer to the Fragment example code (scroll down to the showDetails() method). However, instead of FragmentManager#findFragmentById you should use FragmentManager#findFragmentByTag.
Perhaps something like this:
FragmentManager fragManager = getFragmentManager();
FragmentTransaction xact = fragManager.beginTransaction();
ResultFragment resultFragment = (ResultFragment)fragManager.findFragmentByTag(selectedListener.degree);
if(resultFragment == null) {
mList = resultFragment.newInstance(selectedListener.degree);
xact.add(R.id.view_container, resultFragment, selectedListener.degree);
}
xact.commit();
Be careful, though, if degree varies wildly then this would result in many Fragments contained in the FragmentManger. In this case you should remove the unused Fragments as needed.

Passing an arraylist from an activity to a java class in Android

I have an arraylist in MainActivity and I have a java class which is not an acitivity. How can i pass this arraylist to this class ? My arraylist:
public List<String> types = new ArrayList<String>();
and i filled in MainAcitivity. then i have to use in a java class which is name ExpandableListAdapter. Here is part of the java class which i have to do change in it and i tried to create new activity object but it didn't work.
public class ExpandableListAdapter extends BaseExpandableListAdapter {
public View getChildView(int groupPosition, final int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
MainActivity m=new MainActivity();
Integer[] mIcons = {R.drawable.car_1,R.drawable.car_2,R.drawable.car_3,R.drawable.car_4,R.drawable.car_5,R.drawable.car_6,R.drawable.car_7,R.drawable.car_8,R.drawable.car_9,R.drawable.car_10}
Integer[] mIcons_2 = new Integer [m.types.size()];
for(int i=0; i<m.types.size(); i++){
mIcons_2[i]= mIcons[ Integer.parseInt(m.types.get(i))];
}
final String childText = (String) getChild(groupPosition, childPosition);
if (convertView == null) {
LayoutInflater infalInflater = (LayoutInflater) this._context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = infalInflater.inflate(R.layout.drawer_list_item, null);
}
ImageView iconn =(ImageView)convertView.findViewById(R.id.thumbNail);
iconn.setImageResource(mIcons_2[childPosition]);
TextView txtListChild = (TextView) convertView
.findViewById(R.id.textView);
txtListChild.setTextColor(Color.BLUE);
txtListChild.setText(childText);
return convertView;
}
I created a navigation drawer with an expandable listview also i try to put different icons on every item. This ExpandableListAdapter class is about that but my problem is about how can i pass the types ArrayList from the MainActivity to the ExpandableListAdapter java class? I know that using intent works when pass the values one activity to other activity but i didn't know about passing one activity to a java class.
Pass it using the constructor as follows:
public class ExpandableListAdapter extends BaseExpandableListAdapter {
private final List<String> typesList;
public ExpandableListAdapter(List<String> typesList) {
this.typesList = typesList;
}
...
...
}
Alternatively, you can also use a setter like:
public class ExpandableListAdapter extends BaseExpandableListAdapter {
private List<String> typesList;
public void setTypesList(List<String> typesList) {
this.typesList = typesList;
}
...
...
}
Then you can set the types anytime by calling adapter.setTypesList()
Declare a List in your adapter class
// Declaration :
public List<String> list = new ArrayList<String>();
Constructor will help you to pass the arraylist as params.
ExpandableListAdapter(List<String> types){
this.list = types
}
You can pass the arraylist as follows :
new ExpandableListAdapter(types);
I will suggest you to call the constructor of the non activity class having an array List as its parameter
Lets suppose my Non-Activity class be Class B
B obj=new B(Activity context,types);
And don't forget to declare the same constructor in B class

Categories