Make 3 views visible then hide the others based on a condition - java

I have a RecyclerView item on Java that contains several images that will be visible based on some conditions. However, I noticed if most of the conditions are met, many images will be visible and will be out of bound from its parent layout.
Below illustrates how my intention is for this purpose:
The working code is simple for each image in the RecyclerView, e.g.:
if (iJobType == 1) {
imageView1.setVisibility(View.VISIBLE)
} else {
imageView1.setVisibility(View.GONE)
}
if (iJobType == 2) {
imageView2.setVisibility(View.VISIBLE)
} else {
imageView2.setVisibility(View.GONE)
}
However, I'm not sure what to do to only show 3 images once 3 conditions are met. Let's say iJobType given is 1, 2, 3, 4, 5 ,6; that means 6 images will be shown based on such condition, but right now I only need 3 images to be shown.
I hope it's clear enough, feel free to leave comments below to understand this better. Cheers!

What i would do in this case is:
Firstly I will create a getter setter boolean isConditionmeet in Object class which is used in adapter class.
e.g. list is used in Adapter class as:
private ArrayList<MyObject> list;
and the Object class:
public class MyObject
{
private boolean conditionMeet;
public boolean isconditionMeet()
{
return conditionMeet;
}
public void setconditionMeet(boolean conditionMeet)
{
this.conditionMeet = conditionMeet;
}
}
Now whenever condition will meet to show Item, i will set setconditionMeet(true) for that item and conditionMeet is false for other Items. Lastly update adapter to get changes in UI.

Related

JavaFX Exception in thread "JavaFX Application Thread" java.lang.ArrayIndexOutOfBoundsException when updating search field

I'm writing a small program for a project for Uni and it's basically a library program to manage books anr read/write to JSON file instead of using a database cause it'd be simpler since it's my first proper Java application.
I'm utilizing a TextField to filter a ListView with all the books' titles, and it works, it shows the correct book in the list and updates the corresponding informations on screen when that book is selected, the issue is that even if the program works as intended, it throws an error everytime I update the search field I get an ArrayIndexOutOfBoundsException. The full stack is as follows:
Exception in thread "JavaFX Application Thread" java.lang.ArrayIndexOutOfBoundsException: Index -1 out of bounds for length 7
at javafx.base#19-ea/javafx.collections.transformation.FilteredList.get(FilteredList.java:172)
at com.libraryproject.javalibrary/com.libraryproject.javalibrary.MainViewController.populateDetails(MainViewController.java:200)
at com.libraryproject.javalibrary/com.libraryproject.javalibrary.MainViewController.lambda$initialize$3(MainViewController.java:127)
at javafx.graphics#19-ea/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:457)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at javafx.graphics#19-ea/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:456)
at javafx.graphics#19-ea/com.sun.glass.ui.InvokeLaterDispatcher$Future.run$$$capture(InvokeLaterDispatcher.java:96)
at javafx.graphics#19-ea/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java)
at javafx.graphics#19-ea/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics#19-ea/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:184)
After some googling, some people suggested that when updating the GUI from user input, one should do it in the Application Thread, which to be honest I'm not absolutely sure what that means, but anyway I followed the advice and wrapped the functions that would then update the UI variables in a Platform.runLater(() -> {} , but the issue still remains, and it's the stack above, at this point I have absolutely no idea what the problem could be, so, following the stack posted, let's see the code of the parts that are shown:
I'm using a FilteredList to, well, filter the listrView using the search, here's the code managing that and most of the initialize method:
private FilteredList<Book> filteredBooks;
...
...
// inside the initialize method
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
// Populate the variable we use throughout the program with the data from the JSON file
filteredBooks = new FilteredList<Book>(handleJSON.getBooks());
// Then update the list view for the first time
populateView(filteredBooks);
...
...
// section of code responsible to check for search changes, when found, fires populateView once more, this time with the variable updated.
searchField.textProperty().addListener((obs, oldText, newText) -> {
filteredBooks.setPredicate(book -> {
if(newText == null || newText.isEmpty() || newText.isBlank()) {
return true;
}
String lowerCaseCompare = newText.toLowerCase();
if(book.getTitle().toLowerCase().contains(lowerCaseCompare)) {
return true;
}
return false;
});
Platform.runLater(() -> populateView(filteredBooks));
}); // Listener
...
...
...
// This one handles the selection of an item in the list, when selected, the fields on the other side of the windows will get populated with the respective data from the book based on the id from the list, since they essentialy share the same FilteredList
listView.getSelectionModel().selectedItemProperty().addListener((obs, oldSel, newSel) -> {
Platform.runLater(() -> {
populateDetails(listView.getSelectionModel().selectedIndexProperty().getValue(), filteredBooks);
editButton.setDisable(false);
});
As you can see I wrapped all of the function that will update the ListView and fields in the window with Platform.runLater, but it doesn't seem to help.
Now for the populateView function that fires the first time the program is opened and everytime there's a change in the searchfiield:
public void populateView(FilteredList<Book> booksList) {
// clears the listview to avoid old elements stacking in the list.
listView.getSelectionModel().clearSelection();
listView.getItems().clear();
ObservableList<String> rawTitles = FXCollections.observableArrayList();
for(Book book: booksList) {
rawTitles.add(book.getTitle());
}
listView.setItems(rawTitles);
} // populateView()
And last but not least the populateDetails function that fills the fields about a book based on the list selection:
public void populateDetails(Integer selectedBookID, FilteredList<Book> books) {
Book currentBook = books.get(selectedBookID);
titleValue.setText(currentBook.getTitle());
authorValue.setText(currentBook.getAuthor());
languageValue.setText(currentBook.getLanguage());
genreValue.setText(currentBook.getGenre());
pagesValue.setText(currentBook.getPages().toString());
yearValue.setText(currentBook.getRelease().toString());
if (currentBook.getAvailable()) {
availableRadio.setSelected(true);
} else {
unavailableRadio.setSelected(true);
}
} // populateDetails
Thats basically I tried to use the runLater in different places just to be sure, I still get the same stack, any idea what could cause this?
The stack trace tells you exactly what the problem is. The ArrayIndexOutOfBoundsException occurs when you call get(..) on a FilteredList with the value -1, which you do on line 200 of MainViewController.java, in the populateDetails(...) method. Looking at your code, this line must be the line
Book currentBook = books.get(selectedBookID);
so selectedBookID must be the culprit, having the value -1.
selectedBookID is a parameter passed to the method, and you call the method from line 127 of MainController.java, in a lambda expression in the initialize() method. (Again, this information is in the stack trace.) The value you pass is
listView.getSelectionModel().selectedIndexProperty().getValue()
The documentation tells you explicitly when this happens:
The selected index is either -1, to represent that there is no selection, or an integer value that is within the range of the underlying data model size.
So your populate details needs to handle the case where nothing is selected (probably by clearing the text fields). I think it's cleaner to listen to the selectedItemProperty() instead of the selectedIndexProperty(), as it directly gives you the selected Book (or null if nothing is selected), and you don't have to retrieve the Book from the list:
public void populateDetails(Book currentBook) {
if (currentBook == null) {
titleValue.setText("");
authorValue.setText("");
languageValue.setText("");
genreValue.setText("");
pagesValue.setText("");
yearValue.setText("");
availableRadio.setSelected(false);
unavailableRadio.setSelected(false);
} else {
titleValue.setText(currentBook.getTitle());
authorValue.setText(currentBook.getAuthor());
languageValue.setText(currentBook.getLanguage());
genreValue.setText(currentBook.getGenre());
pagesValue.setText(currentBook.getPages().toString());
yearValue.setText(currentBook.getRelease().toString());
if (currentBook.getAvailable()) {
availableRadio.setSelected(true);
} else {
unavailableRadio.setSelected(true);
}
}
}
Your code is overkill; there is basically no need for the populateView() method. The filtered list will update its contents when you change the predicate, and notify observers that its content has changed. So you should just set the list view's items list to the filtered list directly. Then your listener for the search field only has to update the predicate, and the list view will automatically update.
Delete the populateView() method and update the initialize() method as:
public void initialize(URL arg0, ResourceBundle arg1) {
// Populate the variable we use throughout the program with the data from the JSON file
filteredBooks = new FilteredList<Book>(handleJSON.getBooks());
listView.setItems(filteredBooks);
// ...
// ...
// section of code responsible to check for search changes, when found, fires populateView once more, this time with the variable updated.
searchField.textProperty().addListener((obs, oldText, newText) -> {
filteredBooks.setPredicate(book -> {
if(newText == null || newText.isEmpty() || newText.isBlank()) {
return true;
}
String lowerCaseCompare = newText.toLowerCase();
return book.getTitle().toLowerCase().contains(lowerCaseCompare)
});
}); // Listener
// ...
// This one handles the selection of an item in the list, when selected, the fields on the other side of the windows will get populated with the respective data from the book based on the id from the list, since they essentialy share the same FilteredList
listView.getSelectionModel().selectedItemProperty().addListener(
(obs, oldSel, newSel) -> populateDetails(newSel)
);
}

Shuffling ObservableList fires incorrect change notification

Method FXCollections.shuffle() fires only wasRemoved change notification. As we may know, shuffling isn't only about removing, but removing and adding.
In the documentation we can see:
Shuffles all elements in the observable list. Fires only one change notification on the list.
If I'm not mistaken, a single change can contain both wasAdded and wasRemoved. What a shame wasPermutated isn't being fired with the default ObservableList FX api (or is it?).
Code to test it out:
public class SimpleMain {
public static void main(String[] args) {
ObservableList<Integer> integers = FXCollections.observableArrayList(1, 2, 3, 4);
integers.addListener(initListener());
FXCollections.shuffle(integers);
}
private static ListChangeListener<Integer> initListener() {
return change -> {
while (change.next()) {
if (change.wasPermutated()) {
System.out.println("wasPermutated");
} else if (change.wasRemoved()) {
System.out.println("wasRemoved");
} else if (change.wasAdded()) {
System.out.println("wasAdded");
}
}
};
}
}
Problem
Your code assumes that wasAdded() and wasRemoved() are mutually exclusive, whether you intended that or not. That assumption is wrong. If one or more contiguous elements are replaced then both those methods will return true.
Keep in mind there's a difference between a Change object and a "change". A single Change instance can carry multiple changes. When the documentation says:
Fires only one change notification on the list.
It is not saying only one Change object will be sent to the ListChangeListener. What it's saying is that the Change object will only carry one change. In other words, the Change#next() method will only return true for the first invocation and thus your while loop will only loop once.
Solution
You need to rewrite your code with the knowledge that both wasAdded() and wasRemoved() can both be true. For instance, here's a listener that checks for all types of changes:
private static ListChangeListener<Integer> initListener() {
return change -> {
while (change.next()) {
if (change.wasPermutated()) {
System.out.println("wasPermutated");
} else if (change.wasUpdated()) {
System.out.println("wasUpdated");
} else if (change.wasReplaced()) {
System.out.println("wasReplaced");
} else if (change.wasRemoved()) {
System.out.println("wasRemoved");
} else { // only other change type is "added"
System.out.println("wasAdded");
}
}
};
}
The above uses wasReplaced() which is the same as wasAdded() && wasRemoved(). Note that a check for wasReplaced() must happen before either wasRemoved() or wasAdded() if you use an if-else-if structure. Otherwise the above will suffer the same problem your code has.
If you insert the above into your code and run it you'll see the following output:
wasReplaced
The documentation of ListChangeListener.Change gives a more general explanation (and example) of how to implement a ListChangeListener.
Note the example in the documentation does not check for wasReplaced() specifically. Instead, it processes both removed and added elements in a final else block (after wasPermutated() and wasUpdated() return false). This is possible because getRemoved() and getAddedSubList() will return empty lists if no element was removed or added, respectively. And it's typically the same effect if you process any removed elements and then any added elements as it is if you specially handle replacements. Depending on your use case, however, it may be beneficial to specially handle replacements.
Why not Permutation?
The way they've implemented the shuffle method is:
public static void shuffle(ObservableList list, Random rnd) {
Object newContent[] = list.toArray();
for (int i = list.size(); i > 1; i--) {
swap(newContent, i - 1, rnd.nextInt(i));
}
list.setAll(newContent);
}
Source: javafx.collections.FXCollections, JavaFX 15.
As you can see, the elements are extracted out into an array, the array is shuffled, and then the elements in the list are replaced with the array. That results in a single "replacement change" being fired.
I added one line to your ListChangeListener in the while loop:
System.out.println("change: "+change);
The output I get is:
change: { [1, 2, 3, 4] replaced by [4, 2, 3, 1] at 0 }
wasRemoved
That hints at your mistake. The verb used in the string representation is "replaced".
Getting rid of the 'else's in your listener and adding a check for wasReplaced() you will see that is exactly what you get.

How to select first item without long press using RecyclerView's SelectionTracker

I'm building an application that will allow users to pick from a RecyclerView list, highlighting their choice. The problem is that in order to highlight an item for the first time, a long press is needed. (Afterwards, a short click is enough to do the selection.)
I haven't found anything in the documentation to indicate why this happens.
I'm using SelectionTracker
Specifically following this guide
Here's the code :
https://github.com/marcosholgado/multiselection
Expectations:
I expect the item on the RecyclerView to be selected every time someone short clicks on it.
Reality:
In order to select an item for the first time, the user needs to long press it.
Any Ideas?
Just override SelectionHotspot to return true. That's all you need
fun getItemDetails(): ItemDetailsLookup.ItemDetails<Long> =
object : ItemDetailsLookup.ItemDetails<Long>() {
override fun getPosition(): Int = adapterPosition
override fun getSelectionKey(): Long? = itemId
override fun inSelectionHotspot(e: MotionEvent): Boolean { return true }
}
While I couldn't think of a solution that doesn't involve reimplementing both a MotionInputHandler and a SelectionTracker.Builder (as mentioned in the guide), there is a neat trick to achieve the behaviour you want.
We know that the TouchInputHandler selects items on single click as long as the SelectionTracker is not empty. That means if we have some special key saved in the SelectoinTracker that's not associated with a real list item we practically 'activate' the on single click selection mode this way. However we also have to make sure that our KeyProvider doesn't provide that same special key to keep our data consistent.
So assuming you have picked a special key, say ghostKey, activating and deactivating the selection mode is now a matter of calling mSelectionTracker.select(ghostkey) or mSelectionTracker.clearSelection(). You can then execute these calls however you like, be that having a button that activates and deactivates the selection mode or simply calling that during the hosting view creation process i.e onCreate, onCreateView etc..
If you are using Kotlin you can also define some Extensions that wrap these calls for you, so you'd be able to do things like mSelectionTracker.enable() or mSelectionTracker.disable()
Use this line
selectionTracker.select(item.getSelectionKey());
On this override method
.withOnItemActivatedListener(new OnItemActivatedListener() {
#Override
public boolean onItemActivated(#NonNull ItemDetailsLookup.ItemDetails item, #NonNull MotionEvent e) {
selectionTracker.select(item.getSelectionKey());
return true;
}
})
I solved like this. Keep coding..
In your ViewHolder, set onClickListener and in that check whether you have any selected items using SelectionTracker.hasSelection() and if it returns false, just select that item using SelectionTracker.select(getItemDetails().getSelectionKey())
like this:
itemView.setOnClickListener(v -> {
if (!tracker.hasSelection())
tracker.select(getItemDetails().getSelectionKey());
});
This is what I did:
binding.root.setOnClickListener {
tracker?.let {
if(!it.hasSelection()) it.select(binding.currItem?.uuid!!)
binding.setVariable(BR.selected, it.isSelected(binding.currItem?.uuid))
}
binding.executePendingBindings()
}
I implemented a click listener on the view with the check if the selection tracker has any selected entity or not. If it does not have any selection then I explicitly added the selection and the selection tracker gets enabled automatically.
I think this must the solution you were looking for.
The RecyclerView-selection framework is not designed to handle either of the following scenarios!!
A.) An 'enable multiselect' button that toggles the multi-selection on-and-off.
B.) RecyclerView Adapters that utilize more than one row ViewType that must not be selectable.
*If your scope requires A.) or B.) (above), do NOT use the RecyclerView-selection framework.
Instead, remove it and do the following:
1.) Add a Post-Api data field to your list item model:
var isMultiSelected: Boolean = false // User has highlighted this inbox's row for multi-selection
2.) Create a Listener to communicate from the Adapter to the Fragment/Activity:
class MyFragment : MyFragmentListener {
interface MyFragmentListener {
fun myDataModelOnClick(myDataModel: MyDataModel)
fun engageMultiSelect(selectedDataModelCount: Int)
fun disengageMultiSelect()
}
override fun engageMultiSelect(selectedMyModelCount: Int) {
// Update the fragment view for multi-select engaged conditions
selectedCount_textView.text = selectedMyModelCount.toString()
}
override fun disengageMultiSelect() {
// Update the fragment view for multi-select engaged conditions
}
3.) Update your Adapter.onBindViewHolder() to use the new field
holder.enableMultiSelectionButton.setOnClickListener {
myModel.isMultiSelected = true
myModel.engageMultiSelect(getMultiSelectionCount()) // Enable multi-selection
notifyItemChanged(position)
}
holder.rowConstraintLayout.setOnClickListener {
if (getMultiSelectionCount() > 0) { // Is multi-selection engaged?
myModel.isMultiSelected = !myModel.isMultiSelected
if (getMultiSelectionCount() > 0) { // New Item Count may still be larger than 0
myModelListener.engageMultiSelect(getMultiSelectionCount())
} else {
myModelListener.disengageMultiSelect()
}
notifyItemChanged(position)
} else {
// Normal 'tap' when multi-selection is not engaged
}
}
holder.rowConstraintLayout.setOnLongClickListener {
myModel.isMultiSelected = !myModel.isMultiSelected
if (getMultiSelectionCount() > 0) { // Is multi-selection engaged?
myModelListener.engageMultiSelect(getMultiSelectionCount())
} else {
myModelListener.disengageMultiSelect()
}
notifyItemChanged(position)
true
}
if (myModel.isMultiSelected) {
holder.rowConstraintLayout.setBackgroundColor(R.id.coolcolor)
} else {
holder.rowConstraintLayout.setBackgroundColor(R.id.white)
}

Unable to write an algorithm for filtering out items in a RecyclerView based on a long saved with each item

I have several items in a RecyclerView and each item has a long value saved with it. I'm using FastAdapter as the adapter for my RecyclerView.
Suppose there are 7 items in the RecyclerView with the long values: 11122, 12321, -98811, 8870, -88009, 3398, and -22113.
So, what I want to do is, I want to filter the items based on the above given long values using this logic:
if (l <= 1000) {
// show items with long value <=1000
} else if (l > 1000) {
// show items with long value >1000
}
I tried various things, but nothing worked out.
UPDATE 1: Items here are a sort of different data stored in CardView and then shown in RecyclerView. Each card contains different data, one of which are the above given long values. I want to filter the data based on these long values stored in each card based on the logic given above.
Please help me with this issue and suggest some algorithm or code with which I can achieve this.
With the amount of information given I can only suppose l is a foreign selector value which controls the items to be displayed inside the RecyclerView. Comment below if this is not the case, I will try to correct my answer.
I recommend implementing a custom ViewAdapter, sending in the list of items and the selector variable l using respective methods:
public class ItemsAdapter extends
RecyclerView.Adapter<ItemsAdapter.ItemViewHolder> {
private List<Long> mItemList;
private List<Long> mDisplayItems;
private boolean mAboveThousand = true;
public void setItemList(List<Long> list) {
mItemList = list;
updateDisplayItems();
}
public void setSelectionType(boolean aboveThousand) {
mAboveThousand = aboveThousand;
updateDisplayItems();
}
private updateDisplayItems() {
mDisplayItems.clear();
for(Long item: mItemList) {
if(/*check your contition*/) {
mDisplayItems.add(item);
}
}
notifyDataSetChanged(); //important
}
...
// Rest of implementation
}
Also, I have never used FastAdapter, but I suppose there must be some methods to override if you extend its class.
Update
Since, you are facing problems understanding the basics of using a ViewAdapter, I would recommend learning and implementing a custom ViewAdapter before using any library. Here's a extensive tutorial for how to implement ViewAdapter for RecyclerView.
Now, after you have implemented the ViewAdapter you can use my piece of code to filter out cards. Basically, what the code is doing is saving a list of all the required data inside mItemList, while mDisplayList is a list storing the items to be displayed, which is updated every-time mAboveThousand, which stores the user preference of above or below 1000, is set. Now this mDisplayList must be used to inflate data inside the RecyclerView.
Even your very basic code there would work. You can count the number of items in that range and return the number in that range. I suggest you try to do this without FastAdapter because the core concept of parsing the data based on a filter value is rightly perfectly solid. You can iterate the loop and count them, and you can iterate the loop and return the nth item.
If you do want to keep using FastAdapter, it has a built-in filter functionality (see point number 5 in the README of the project. Note that the filter method should be called after withFilterPredicate and not before as shown there).
EDIT - after you pointed out that I misunderstood you before - here is my updated proposed instructions:
You need to resolve the logics of which set you want to display (using the checkboxes in the dialog you mentioned in the comment) and pass that information onto the filter, for example:
boolean displayUnderThreshold = //put the logic here - true if you want <1000
fastAdapter.filter(Boolean.toString(displayUnderThreshold));
And where you set the adapter (before the above line is called) have:
final long threshold = 1000;
fastAdapter.withFilterPredicate(new IItemAdapter.Predicate<GRModeClass>() {
#Override
public boolean filter(GRModeClass item, CharSequence constraint) {
boolean displayUnderThreshold = new Boolean(constraint.toString());
return (displayUnderThreshold ^ (item.l<threshold)); //false to remove from list
}
});
Old answer From when I thought you wanted to filter the items according to their ms long values, using an external l long indicator:
In your code, assuming your app does get to the if you mentioned in the question when it should - remove the fastItemAdapter.clear(); and instead of the for loop with the if inside it write
fastItemAdapter.filter(Long.toString(l));
and somewhere before that, preferably where you set the adapter (most likely in the onCreate of MainActivity) add the following:
final long threshold = 1000;
fastAdapter.withFilterPredicate(new IItemAdapter.Predicate<GRModeClass>() {
#Override
public boolean filter(GRModeClass item, CharSequence constraint) {
long indicator = new Long(constraint.toString());
return (item.ms<threshold && indicator>=threshold) || (item.ms>=threshold && indicator<threshold) ;
}
});
(Assuming here that GRModeClass is your items' class and that the long ms is the long you referred to that should determine whether the )
I guess your class is like
public Class ListItem {
// .. Some other attributes
public long l;
}
Now I hope you've some function which is called when you're putting a filter in your RecyclerView. Let the function name is toggleFilter.
public void toggleFilter(long l) {
if(l <= 1000) {
fastAdapter.withFilterPredicate(new IItemAdapter.Predicate<Item>() {
#Override
public boolean filter(ListItem item, CharSequence constraint) {
if(item.l <= 1000) return true;
else return false;
}
});
} else if (l > 1000) {
fastAdapter.withFilterPredicate(new IItemAdapter.Predicate<Item>() {
#Override
public boolean filter(ListItem item, CharSequence constraint) {
if(item.l > 1000) return true;
else return false;
}
});
}
// Finally call notifyDataSetChanged
fastAdapter.notifyDataSetChanged();
}
You can filter while fetching from firebase.
l <= 1000
firebaseDatabase.child(key).orderByChild("long_value_key_in_firebase").endAt(1000);
l > 1000
firebaseDatabase.child(key).orderByChild("long_value_key_in_firebase").startAt(1000);

How to avoid redundant coding with JCheckBoxes in Java

I have a set of classes that implement a particular interface and I have a set of checkboxes. I want to throw an error if no checkboxes are selected. If atleast one or more checkboxes are selected, then it should create objects associated with that checkbox.
This is how I done.
interface U { ... }
class A implements U { ... }
class B implements U { ... }
class C implements U { ... }
class Main {
//....
//....
public void findSelectedCheckBoxesAndCreateObjects() {
if(!(checkboxA.isSelected() || checkboxB.isSelected() || checkboxC.isSelected()) {
System.out.println("No checkboxes selected");
return;
}
//if any selected, create associated object
if(checkboxA.isSelected()) new A(file);
if(checkboxB.isSelected()) new B(file);
if(checkboxC.isSelected()) new C(file);
}
}
Now I have 3 problems.
This is just a sample code. Original has 8 checkboxes and classes with more coming.
I can't keep adding || checkboxD.isSelected() every time I have a new class for checking it.
Same thing. I can't keep adding if(checkboxD.isSelected()) new D(file); for every class.
It is very inelegant. Can I have some kind of loop that removes the redundant code?
Please give me your suggestions.
Thank you.
You should use a collection structure to hold your checkboxes and those related classes.
Using a Map you could do something like this:
Map <JCheckBox,Class<U>> uCheck = new HashMap<JCheckBox,Class<U>>();
// add your checkboxes and U-classes to the map
uCheck.put(checkBoxA, A.class);
Now, it's quite easy to get a collection of the classes that need to be instantiated based on the checkbox status:
public Collection<Class<U>> getEnabledClasses(<JCheckBox,Class<U>> checkMap) {
List<Class<U>> result = new LinkedList<Class<U>>();
for (Map.Entry<JCheckBox,Class<U>> entry:checkMap.entrySet()) {
if (entry.getKey().isSelected()) {
result.add(entry.getValue());
}
}
}
Now, a call to getEnabledUs(uCheck) returns a collection of the selected classes. If the collection is empty, there's no selection, hence nothing to do.
for (Class<U> u:getEnabledClasses(...)) {
Constructor<U> cons = u.getConstructor(...);
U instance = cons.newInstance(fileparameter);
instance.doSomething(...);
}
That should get you started.
(*) Disclaimer: this is non-tested code. Rather pseudo-code with crisp detail only where needed.

Categories

Resources