I am trying to impement a school planner app. There is a timetable overview implemented as a ListView for each day wrapped into a Tab Layout. So the user can switch between days Monday to Friday and gets his timetable for the specific day.
public class TimetableAdapter extends ArrayAdapter<Lesson> {
private List<Lesson> lessonList; // The model
...
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
...
view = inflater.inflate(R.layout.timetable_row, null);
Lesson currentLesson = lessonList.get(position);
// Checks if selected Tab (context.getDay()) correspondends to
// currentLesson's day. If so the lesson will be rendered into
// the appropriated ListView. So if the user selects the Monday Tab
// he only wants to see the lessons for Monday.
if (context.getDay() == currentLesson.getWeekDay().getWeekDay()) {
fillView(currentLesson, holder); // render Lesson
}
...
return view;
}
private void fillView(Lesson currentLesson, ViewHolder holder) {
holder.subject.setText(currentLesson.getSubject().getName());
}
public class TimetableActivity extends Activity implements OnTabChangeListener {
public void onCreate(Bundle savedInstanceState) {
....
timetableAdapter = new TimetableAdapter(this, getModel());
}
private List<Lesson> getModel() {
return timetable.getLessons();
}
public void onTabChanged(String tabId) {
currentTabName = tabId;
if (tabId.equals("tabMonday")) {
setCurrentListView(mondayListView);
}
else if (tabId.equals("tabTuesday")) {
// Checks tabTuesday and so on....
...
}
}
private void addLesson() {
timetable.addLesson(new Lesson(blabla(name, weekday etc.))); // blabla = user specified values
timetableAdapter.notifyDataSetChanged();
}
So basically if the user adds a lesson he specifies some parameters like corresponding weekday, name etc. This is represented by blabla.
The problem is that, because I am using only one ArrayList for my data, wether it's a subject on Monday or on Tuesday the lesson e.g. for Monday is rendered on my Tuesday's ListView as an empty row, because getView(...) is called for each item in lessonList and it just returns a new View(), if the weekday isn't the desired one, I think.
One solution might be creating 5 ArrayLists and 5 ArrayAdapters for the appropriated weekdays. So the lessons on Monday will be in ArrayList mondayList, and the adapter will be bound to this list. But this is somewhat unflexible.
Is there a better solution?
Thanks in advance.
Instead of using ArrayAdapter, use SimpleAdapter. It is far more flexible for what you want to display. Second keep your ArrayList of all the appointments out of the Adapter, create some sort of filtering method to copy applicable objects and pass this new list to the SimpleAdapter.
Related
I have pondered on this for days now, I need help. I have 2 fragments in a viewpager. They are both showing the same values despite feeding them with different data.
Link to image
Fragment A has a spinner dropdown of countries same as Fragment B. Fragment A is supposed to show list of Hospitals when country is selected, Fragment B is supposed to show list of Schools depending on country selected.
PROBLEM
When I select a country in Fragment A, I get the list of hospitals in fragment A. When I swipe to fragment B, I find the list of hospitals which are supposed to be in A displayed. When I select a country in B, the list refreshes and I get the list of schools as should be, but when I swipe back again to fragment A I find the same list of schools instead of list of hospitals previously selected. I am using dagger2 with databinding.
Below code depicts one scenario but they are basically the same just different adapters and also different layouts.
CODE
private void initRecyclerview() {
binding.rvSchools.setHasFixedSize(true);
adapter = new SchoolsAdapter(getContext());
binding.rvSchools.setLayoutManager(new LinearLayoutManager(getContext()));
binding.rvSchools.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
private void countries() {
viewModel.getCountries().removeObservers(getViewLifecycleOwner());
viewModel.getCountries().observe(getViewLifecycleOwner(), new Observer<Resource<List<Countries>>>() {
#Override
public void onChanged(Resource<List<Countries>> countriesList) {
switch (countriesList.status) {
case SUCCESS:
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(getApplicationContext,
R.layout.simple_spinner_item, countriesList);
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
binding.spinner.setAdapter(spinnerAdapter);
spinnerAdapter.notifyDataSetChanged();
break;
}
}
});
binding.spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
String country = countriesList.get(position);
schools(country);
}
#Override
public void onNothingSelected(AdapterView<?> parent) {}
});
}
private void schools(String country) {
viewModel.getSchoolsHospitals(country, "School").removeObservers(getViewLifecycleOwner());
viewModel.getSchoolsHospitals(country, "School").observe(getViewLifecycleOwner(), new Observer<Resource<List<SchoolsHospitals>>>() {
#Override
public void onChanged(Resource<List<SchoolsHospitals>> schoolsList) {
switch (schoolsList.status) {
case SUCCESS:
binding.setSchools(schoolsList.data);
}
}
});
}
This is a rather odd behaviour considering how I wanted to re-use code. I was using the same viewModel class because the data is the same. After creating a separate viewModel class for Schools i.e a duplicate ViewModel just different class names, the respective contents were set as needed.
If there is a better approach, it will be much appreciated.
So the spinner I have currently picks from a programmatic array which fetches names and images based on what's in the array.
What I need is to only pick from items in the array if they exist based on the layouts I have.
eg. I have multiple accounts in my main activity list. I only want to be able to pick an account in my spinner based on the accounts I have available (user only has 2 of 3 accounts from the array, thus only display 2 items in the spinner not all 3)
Here's my current spinner code as well as the array:
SpinnerActivity:
public class SpinnerActivity extends AppCompatActivity {
private ArrayList<AccountItem> mAccountList;
private AccountAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_transactions);
//the account_spinner is being pulled from the fragment_transactions xml
initList();
Spinner spinnerAccount = findViewById(R.id.account_spinner);
mAdapter = new AccountAdapter(this, mAccountList);
spinnerAccount.setAdapter(mAdapter);
spinnerAccount.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
AccountItem clickedItem = (AccountItem) parent.getItemAtPosition(position);
String clickedAccountName = clickedItem.getAccountName();
Toast.makeText(SpinnerActivity.this, clickedAccountName + " selected", Toast.LENGTH_SHORT).show();
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
/**
*This is the array, I need this to link each item to their respective accounts
*that are available
**/
private void initList() {
mAccountList = new ArrayList<>();
mAccountList.add(new AccountItem("Account1", R.drawable.account1_icon));
mAccountList.add(new AccountItem("Account2", R.drawable.account2_icon));
mAccountList.add(new AccountItem("Account3", R.drawable.account3_icon));
}
}
I just need an idea of where to start. As it stands I don't see a way to give my array items individual IDs so I'm not sure if I need to change my array?
You just change the array. If you need to fetch the account list asynchronously, you can call mAdapter.notifyDataSetChanged() in the async completion callback to tell the Adapter that its backing array has changed.
I am having adapter class, In that, I need to pass invoiceId to an Activity Class. I have seen some example like pass-through interface, but I lost track on following the code procedure.
Here Is My Adapter Class extends BaseAdapter
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
companyName = ct.getSharedPreferences("prefs", 0);
Log.d("test", "" + deliveryListBeans.size());
LayoutInflater inflater = (LayoutInflater) ct.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.list_vew_for_delivery_order, null);
TextView invoice = (TextView) v.findViewById(R.id.invoice);
final TextView delivery = (TextView) v.findViewById(R.id.do_delivery);
final DeliveryListBean dlb = deliveryListBeans.get(position);
invoice.setText(dlb.getInvoiceNo());
}
delivery.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ct.startActivity(new Intent(ct, EmployeesListForPopUp.class));
DeliveryOrdersListAdapter deliveryOrdersListAdapter=new DeliveryOrdersListAdapter(EmployeesListForPopUp.this);
}
});
}
Here is My Activity Class
public class EmployeesListForPopUp extends Activity {
private List<EmployeeIdNameBean> employeeIdNameBeans = new ArrayList<EmployeeIdNameBean>();
ListView listView;
SharedPreferences companyName;
EmployeePopUpAdapter employeePopUpAdapter;
private ImageView img1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_employees_list_for_pop_up);
I need to get invoiceId from Adapter Class. How?
You need to pass context of the activity in adapters constructor.
Then set activity.invoiceid value in clickevents of adapter.
One simple way is that you write a method in MainActivity
public void setInvoiceId(int invoiceId) {
// do what you want with invoiceId
}
and pass the instance of your activity to adapter
DeliveryOrdersListAdapter adapter = new DeliveryOrdersListAdapter(EmployeesListForPopUp.this);
and get it in your adapter and keep it
EmployeesListForPopUp myActivity;
public MyAdapter(EmployeesListForPopUp activity) {
myActivity = activity;
}
and where you need to pass invoiceId just call the method of main activity
myActivity.setInvoiceId(invoiceId);
General way of implementing it:
In the adapter class, where you set text to invoice TextView, you also can add a tag to it. Put attention - despite every item in the list is build from the same prototype, the tag (as well as text) will be uniq. The best way is to use "position" as value of the tag: invoice.setText(dlb.getInvoiceNo());
invoice.setTag(Integer.valueOf(position).toString());
You need to make your items in the list clickable (this is out of the scope of this question). So, when you click on some item - you can retrieve any data it has, and specifically tag - getTag();.
Then you send Intent to other activity, providing the tag as extra message. So that activity will "know" which item in the array list it is related to (i.e. tag == position, right?). And continue from there.
I implemented simple project that illustrates it. This project is simple demo and illustration of working with ArrayList adapter,
displaying the item in the ListView, clicking on some item and display relevant data in separated activity. Please download it and try (min API 21). Basic description is available in README file.
The project is here on the GitHub:
(corrected path)
https://github.com/everall77/ArrayListSimpleExmpl
I am still stuck with this issue, can anyone help. It seems that my problem is that I cant update the data list. I have tried every solution that I've searched for on google etc.. but half the time i'm not even sure that I'm doing the correct thing.
I've used the onResume() to call notifyDataSetChanged, it didn't work. I've tried putting a refresh method into the adapter which i then called in OnResume(). Again it didn't work. Some people suggest clearing the adpater (adapter.clear();) in onResume and then using the addAll() function to relist the data but nothing works.
There has to be a simple solution to this. I have literally been stuck on this for 2 days now. very frustrated.
Here's my Fragment code again...
enter code here
public class SavedAppFragment extends ListFragment {
private static final String TAG = "AppClicked"; //DEBUGGER
private ArrayList<App> mSavedApps;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Populate the ArrayList
mSavedApps = SavedAppData.get(getActivity()).getApps();
AppAdapter adapter = new AppAdapter(mSavedApps);
setListAdapter(adapter);
}
//LIST ITEM CLICKED: /*Control what happens when list item is clicked: I.E. Load up a quiz while putting an EXTRA key containg the package name of the App to be launhced should the user get the question correct */ #Override public void onListItemClick(ListView l, View v, int position,long id) { //Return the crime for the list item that was clicked App c = ((AppAdapter) getListAdapter()).getItem(position); Log.d(TAG, "was clicked");
//Start the Activity that will list the detail of the app
Intent i = new Intent(getActivity(), Quiz_Activity.class);
String name = c.getPackage();
i.putExtra("packagename", name);
startActivity(i);
}
private class AppAdapter extends ArrayAdapter {
private ArrayList<App> mSavedApps;
public AppAdapter(ArrayList<App> apps) {
super(getActivity(), 0, apps);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
//If we weren't given a view, inflate one
if (null == convertView) {
convertView = getActivity().getLayoutInflater().inflate(R.layout.list_item_app, null);
//((AppAdapter) getListAdapter()).notifyDataSetChanged();
}
((AppAdapter) getListAdapter()).notifyDataSetChanged();
//Configure the view for this crime
App c = getItem(position);
TextView nameTextView = (TextView) convertView.findViewById(R.id.app_name);
nameTextView.setText(c.getName());
// nameTextView.setText(applicationInfo.loadLabel(packageManager));
TextView packageTextView = (TextView) convertView.findViewById(R.id.app_package);
packageTextView.setText(c.getPackage());
CheckBox appCheckBox = (CheckBox) convertView.findViewById(R.id.app_checked);
appCheckBox.setChecked(c.isChecked());
//Return the view object to the ListView
return convertView;
}
}
}
THANKS!!!
When you return to Activity B, the previous Activity B hasn't been destroyed. Thus, it skips the onCreate. Move all of the stuff you want to make sure happens every time into the onResume. I think you want to make your Adapter a class variable (I'll call it mAdapter) in onCreate, and add code that will get data from the list directly. If you need to do something, put a "refresh" function in the adapter. I'm assuming you have a custom Adapter, because I've never heard of AppAdapter. If you don't, then extend AppAdapter and add that functionality. Thus, your onCreate should look like this:
mAdapter = new AppAdapter(mSavedApps);
setListAdapter(mAdapter);
Your onRefresh could update the data contained in the adapter by some new update function, like so:
mAdapter.update(SavedAppData.get(getActivity()).getApps());
What i want to:
I want to add a swipe or what i learned it's named on android fling, to my app.
I have a dynamic number of views, the number is the amount of dates from an ICS file which i parse, i want to make a swipe effekt on all of these views together.
But if i have let's say 12 of these each having a ListView with 8 items (max) it would use a lot of memory i guess. So i would like that only the 2 before current selected view and the 2 after to be initialized.
What i have tried:
In my search i stumpled around this stackoverflow question which mentions HorizontalPager. But i dont know to make it work with a number of ListView's and load them dynamically.
I tried a ViewGroup and then add and remove a ListView but it's not working, it display's the ViewGroup but not the ListView
public class HourView extends ViewGroup
{
private ListView listView;
/**
* #param context
*/
public HourView(Context context)
{
super(context);
init(false);
}
/**
*
* #param context
* #param day the current day
*/
public HourView(Context context, String day,
boolean shouldBeVisible)
{
super(context);
this.day = day;
init(shouldBeVisible);
}
private void init(boolean shouldBeVisible)
{
if (shouldBeVisible)
{
listView = new ListView(getContext());
if (day == null)
{
day = Event.dtFormatDay.format(new Date());
}
new GetEvents(day).execute();
addView(listView);
}
else
{
removeAllViews();
listView = null;
}
}
}
The GetEvents() is a AsyncTask (a class inside the viewgroup class) that gets some events from a local database, the code is the onPostExecute is as follows
protected void onPostExecute(String errMsg)
{
if (errMsg != null)
{
listView.setAdapter(new SkemaHoursRowAdapter(getContext(), eventItems));
listView.requestLayout();
addView(listView, layoutParams);
}
}
eventItems is an array i parse to my custom rowadapter (which i know works). The view group is displayed but the listView is not.
Any suggestions??
I ended up not using a viewgroup instead i made a loader-listadapter which i display if the correct list has not been updated yet.
So instead of extending ViewGroup it extends ListView and it's adapter get set at at first to the load-adapter and when loaded it sets it to the correct listview.