My Adapter requires a context in order to apply resources to views, therefore when instantiating it, I might do the following within my Activity:
myAdapter = new MyAdapter(this);
As my adapter also needs data from an activity, I might do this:
myAdapter = new MyAdapter(myItemsArrayList,this);
As my adapter might also need to know which items in the ArrayList are selected, I might pass it that list too:
myAdapter = new MyAdapter(myArrayItemsList,mySelectedItemsArrayList,this);
And as there may be other states (e.g. whether to display photos in a list of people, the constructor call is starting to get quite lengthy:
myAdapter = new MyAdapter(myArrayItemsList,mySelectedItemsArrayList,myPreference1,myPreference2,this);
Given that the only place this adapter will be used is from a particular activity, how bad would it be to just make those attributes in the activity public, so that I can access them via the activity that has been passed (e.g myActivity.myArrayItemsList)?
Many thanks in advance for any advice!
Given that the only place this adapter will be used is from a particular activity, how bad would it be to just make those attributes in the activity public, so that I can access them via the activity that has been passed (e.g myActivity.myArrayItemsList)?
That's a bad code and bad behavior. You're code will be tightly coupled. And usually, you will borrow the same behavior to your next project.
Instead of passing each state to your constructor, you can simplify it by passing a State object to your adapter. Create the State class something like this:
public class State {
List<String> selectedItems;
boolean displayPeople;
}
then you can create a simple constructor like this:
State state = new State();
state.selectedItems = mSelectedItems;
state.displayPeople = true;
myAdapter = new MyAdapter(items, state, this);
So, whenever you need to update a new state, you just need to add it the State class and update the Adapter according to it.
Considering you are using the Item object for myArrayItemsList.
So your list should look like this:
ArrayList<Item> myArrayItemsList = new ArrayList();
and then you want to add the selected items in the list you could add a boolean to the Item object ex:
public class Item {
private String itemName;
private boolean selected = false;
public Item(){}
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName= itemName;
}
public boolean isSelected () {
return selected;
}
public void setSelected(boolean selected) {
this.selected= selected;
}
}
and just check your item list in the adapter if the item is selected.
So your adapter would only pass two parameters:
MyAdapter myAdapter = new MyAdapter(myArrayItemsList, this);
But then again you want to pass only one parameter in adapter, you can set your ArrayList to static
public static ArrayList<Item> myArrayItemsList = new ArrayList();
and pass only this your adapter
MyAdapter myAdapter = new MyAdapter(this);
used the static ArrayList in your adapter but it is not advisable using those static data because the data could be Garbage Collected in the memory.
Related
I have a arraylist in my fragment class. I pass the array to the adapter. After the adapter, I pass the array to another fragment. Now in the current fragment, I change a value of the array object. I want this change to change in the first fragment array. How is this possible?
look at the example code:
public class Fragment1 extends Fragment {
ArrayList<Model> array = new ArrayList<>();
// send array to Adapter
}
public class Adapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
ArrayList<Model> array = new ArrayList<>();
array.addAll(getArrayFromFragment1); // use this.array = getArrayFromFragment1 // I don't want to use (this)
// send array to Fragment2
}
public class Fragment2 extends Fragment {
ArrayList<Model> array = new ArrayList<>();
array.addAll(getArrayFromAdapter); // use this.array = getArrayFromAdapter // I don't want to use (this)
// NOW! update object value from array --> array.get(10).setUserPhoneNumber(987654321)
// i need update automatically Fragment1 array object 10 UserPhoneNumber
}
In instagram app:
fragment 1 --> gragment 2 --> explore tab --> fragment 3 like one post --> back to fragment 1 same post liked. how do this relations?
I would use a separate class with a static ArrayList so you only need to use a single source for the data and can read it from any class/fragment.
public class StaticUtils {
public static ArrayList<Model> models = new ArrayList<>();
//or make it private and add static getter/setter methods here
}
public class Fragment2 extends Fragment {
StaticUtils.models.get(10).setUserPhoneNumber(987654321)
}
I am making a frisbee logger and have an ArrayList of Team objects. Each Team has an ArrayList of Player objects. Everything is using Serializable properly to be sent using Intent.
In my main activity I am displaying the list of Team objects in a ListView and an option to add another Team (only a name is needed). Once a Team is selected I pass the object to another activity using Intent. On this second activity I have it display the list of Player objects and have fields to enter another player object into the passed list.
When I return to the main activity and go back to the add Player activity, what I have added is gone.
I cannot use static because there is obviously more than one Team object. I think passing back the changed ArrayList could work but that seems a little lame, time-consuming, and frustrating.
Is there a built-in way in Android Studio that does this or am I on my own?
Note: I am not using SQLite as suggested in the comments
There's not a whole lot to show on this but here it is I guess:
MainActivity.java
private static ArrayList<Team> listOfTeams = new ArrayList<>();
private static ArrayList<Game> listOfGames = new ArrayList<>();
private ListView gameList, teamList;
.....
teamList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Team t = (Team)teamList.getItemAtPosition(position);
viewTeam(t);
}
});
.....
//Item select in teamList. Start the TeamViewActivity
public void viewTeam(Team t)
{
Intent i = new Intent(this, TeamViewActivity.class);
i.putExtra("teamView",t);
startActivity(i);
}
TeamViewActivity.java
private Team team;
private ListView rosterList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_team_view);
rosterList = (ListView) findViewById(R.id.playerList);
Intent i = getIntent();
Bundle extras = i.getExtras();
if(extras!=null)
{
if(extras.get("teamView")!=null)
{
team = (Team) extras.get("teamView");
}
}
populateRosterList(team.getRoster());
}
public void addPlayerToRoster(View view)
{
String checkFirst = ((EditText) findViewById(R.id.firstText)).getText().toString();
String checkLast = ((EditText) findViewById(R.id.lastText)).getText().toString();
String checkNumber = ((EditText) findViewById(R.id.numberText)).getText().toString();
if(!checkNumber.equals(""))
{
team.addPlayer(checkFirst, checkLast, Integer.parseInt(checkNumber));
((EditText) findViewById(R.id.firstText)).setText("");
((EditText) findViewById(R.id.lastText)).setText("");
((EditText) findViewById(R.id.numberText)).setText("");
populateRosterList(team.getRoster());
}
}
public void returnToMain(View view)
{
Intent i = new Intent(this, MainActivity.class);
i.putExtra("teamView", team);
startActivity(i);
}
private void populateRosterList(ArrayList<Player> list)
{
ArrayAdapter<Player> adapter = new ArrayAdapter<>(this,
R.layout.activity_list, R.id.genericText, list);
rosterList.setAdapter(adapter);
}
Consider your concept:
You serialize an object, i.e. you transform it into a transferrable format which is then copied over to the other activity and reconstructed as a new instance.
Consequently, you alter another instance, which is not available in the previous activity, if you do not return it - again, serialized - and finally reconstruct and copy it back into the respective instance.
What you need is a shared memory storage in your application, which can alter and retrieve data cross-activity OR a proper data routing using Intents w/ ISerializable.
Options:
Always serialize objects and pass and copy them around.
-> No multithreaded alteration, possibly slow, unbeautiful
Singleton application with global data storage ir Context Object (I do NOT recommend the due to memory management and Garbage
Collection inbetween Activity Switches BUT for consistency I'd
wanted to mention this option)
SQLite3
-> Quick, Simple and Scalable, But a bit cumbersome to get started with
Any other file-structure stored and maintained in the data folder
-> I'd expect a lot of boilerplate code here, and low performance
Webservice and remote database
Proper component setup, i.e. initialize all accessing components in your software with the appropriate reference to the data structs using for example fragments (Thanks to #mismanc, I actually missed that option initially)
In general you could abstract all that away using services and repositories, which allows you to under-the-hood test options like 3. 4. And 5. and find your best solution, and in addition, keeo the accessing code simple and clean.
in your case, you can use startActivityForResult instead of startActivity, then get your modified Team object from onActivityResult(int requestCode, int resultCode, Intent data) to update your list.
startActivityForResult example
You can use fragments. You hold the list in the MainActivity and pass its reference to ShowListFragment and AddPlayerFragment by interfaces. And you can also do other operations over them. If you dont want to use json or sqlite it can be a good way for you.
MainActivity.java
public class MainActivity extends Activity implements ShowListener{
public interface ShowListener{
ArrayList<Team> getTeamList();
}
private ArrayList<Team> listOfTeams = new ArrayList<>();
#Override
public ArrayList<Team> getTeamList() {
return listOfTeams;
}
}
ShowListFragment.java
public class ShowListFragment extends Fragment {
private ArrayList<Team> listOfTeams;
private ShowListener listener;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
listener = (ShowListener)getActivity();
listOfTeams = listener.getTeamList();
}
}
As #Kingfisher Phuoc mentioned you could use srartActivityForResult in case you don't want to change your approach.
Otherwise I will suggest you use either :
SharedPreference to store your arraylist object (by converting the arraylist to json then store it as string in json format). In the PlayerActivity you retrieve the data, manipulate it then save it. see this post
SQLite
So I've debugged my program and have found that the part of my program is updating, whilst another isn't.
I have a method:
public void storeApplication(String name, String item){
Application app = new Application(name, item);
peopleAttending.add(app);
}
The debugger reports that an object is contained in the LinkedList (peopleAttending).
In another method:
public void populateListView() {
int noOfPeopleAttending = peopleAttending.size();
String noPeopleAttending = String.valueOf(noOfPeopleAttending);
Toast.makeText(GuestsAttending.this, noPeopleAttending, Toast.LENGTH_SHORT).show();
}
This method can be called after the previous one and states that there isn't an object within the LinkedList.
I've checked the object references just to make sure that they are pointing at the same reference and they are.
Any help would be greatly appreciated.
EDIT: Entire Class:
public class GuestsAttending extends Activity {
private LinkedList<Application> peopleAttending = new LinkedList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_guests_attending);
populateListView();
}
public void storeApplication(String name, String item){
Application app = new Application(name, item);
peopleAttending.add(app);
}
public void populateListView() {
// GuestsAdapter adapter = new GuestsAdapter(this, peopleAttending);
// ListView listView = (ListView) findViewById(R.id.listView);
// listView.setAdapter(adapter);
peopleAttending.size();
int noOfPeopleAttending = peopleAttending.size();
String noPeopleAttending = String.valueOf(noOfPeopleAttending);
Toast.makeText(GuestsAttending.this, noPeopleAttending, Toast.LENGTH_SHORT).show();
}
Second Edit:
Java Booking Screen Method:
public void saveBookingInfo(View view) {
GuestsAttending sendApplication = new GuestsAttending();
EditText applicantNameText = (EditText) findViewById(R.id.applicantNameTextField);
EditText itemToBurnText = (EditText) findViewById(R.id.itemToBurnTextField);
String appName = applicantNameText.getText().toString();
String appItemToBurn = itemToBurnText.getText().toString();
if (appItemToBurn.isEmpty() || appName.isEmpty()) {
Toast.makeText(BookingScreen.this, "Please fill in all fields.", Toast.LENGTH_SHORT).show();
}
else {
sendApplication.storeApplication(appName, appItemToBurn);
}
}
GuestsAttending Java Class: -- See Above.
Useful hint: It's really popular to set type of List as a List<> interface from java.util package instead of LinkedList<> itself.
Anyway, i am pretty sure that storeApplication method is not automatically triggered before onCreate method ran by Activity framework. Maybe your debugger is stopoing on it in different order (because of using threads or smth), but you should to log some invoke. Try to find it out.
I've found out what the problem is:
When I submit the booking information, it runs all the necessary methods. However, when the "storeApplication()" method has finished executing, the ArrayList 'empties' all the objects out.
I only noticed this when I used breakpoint and tried running the method twice, on the second time I entered booking details, the ArrayList stated it was empty.
I'm going to see if I can try and store the ArrayList in a more secure place.
// use case 10b alternate version
// caches a read comment temporarily
public void testCacheReadComment2() throws Throwable{
runTestOnUiThread(new Runnable()
{
#Override
public void run(){
CommentBuilder commentBuilder = new commentBuilder();
Comment comment = commentBuilder.createTopTestComment();
//browse button on main screen
((Button)activity.findViewById(ca.ualberta.cs.team5geotopics.browseButton)).performClick();
//the ListView for the custom adapter
ListView listView = (ListView) activity.findViewById(ca.ualberta.cs.team5geotopics.commentList);
//the custom adapter on the physical screen
ArrayAdapter<Comment> adapter = (ArrayAdapter<Comment>) listView.getAdapter();
adapter.add(comment);
adapter.notifyDataSetChanged();
View view = adapter.getView(adapter.getPosition(comment), null, null);
ViewAsserts.assertOnScreen(listView, view);
//this is the button to view the Top Level comment in the list
ViewAsserts.assertOnScreen(view, (Button) view.viewTopLevelComment);
((Button)view.viewTopLevelComment).performClick();
// is there a way I can get references to the objects
// already instantiated in the test thread?
CacheController cc = activity.getCacheController();
assertTrue(cc.getHistory().contains(comment));
}
});
}
We are using a test driven development style in order to code our project for school. In this test I am trying to prove that after a user views a comment from the list in the adapter, that this comment is cached in a history cache. I'm a little confused about some things and I would like some help, because it would be great if I knew there were no obvious flaws in my test case. these are my questions:
View view = adapter.getView(adapter.getPosition(comment), null, null);
Will this line return the view that is associated with the comment stored in the array adapter? My ArrayAdapter is going to follow a holder patter and I'm not sure if this is the proper way to get access to the buttons I need to mimic the user viewing the comment.
CacheController cc = activity.getCacheController();
I have a CacheController object that is instantiated upon the onCreate() method in our main activity. Now I want to reference this CacheController to see if the history is updated properly. I was just making a new CacheController and mimicking the actions of the CacheController in the test method, but I want to test what happens to my data on the UIthread. So, how do I reference objects in the UI thread?
View view = adapter.getView(adapter.getPosition(comment), null, null);
Will this line return the view that is associated with the comment
stored in the array adapter?
I think it should work, but I don't understand why would you want to access the View.
My ArrayAdapter is going to follow a holder patter and I'm not sure if
this is the proper way to get access to the buttons I need to mimic
the user viewing the comment.
The ArrayAdapter is usually used for a ListView. You should just let ListView handle the click capturing and tell you which element was clicked.
So, how do I reference objects in the UI thread?
You have 2 solutions for this that come to my mind right now:
1) Pass the CacheController instance, for example:
public class YourClass {
private final CacheController cacheController;
public YourClass(final CacheController cacheController) {
this.cacheController = cacheController;
}
public void testCacheReadComment2() throws Throwable {
CacheController cc = this.cacheController;
}
}
2) Singleton: make the CacheController static and put an accessor, for example:
public class CacheController {
private final CacheController instance = new CacheController();
public static CacheController getCacheController() {
return instance;
}
}
In both cases you should be aware about potential multi-threading issues because you're spawning new threads that all share same CacheController instance.
I do use this code for refill ListView with data after they change
// ListView lv;
// MyListAdapter la;
// DataClass dc;
dc.remove(object_id);
lv.setAdapter(la);
Is this the best way since we can't use notifyDataSetChanged() which is available only in ArrayAdapter ?
Solution
//MyListAdapter.java
private ArrayList<DataSetObserver> observers = new ArrayList<DataSetObserver>();
public void registerDataSetObserver(DataSetObserver arg0) {
observers.add(arg0);
}
public void unregisterDataSetObserver(DataSetObserver arg0) {
observers.remove(arg0);
}
public void notifyDataSetChange() {
for (DataSetObserver d : observers) {
d.onChanged();
}
}
public void remove(int position) {
[DataClass object].remove(position);
notifyDataSetChange();
}
For a CursorAdapter I use the following code:
mAdapter.getCursor().requery();
If you are using custom adapter or as you commented: only want ListAdapter as member variable. Instead of using private CustomAdapter mAdapter; (which i would recommended to avoid creating unnecessary objects).
You can use DataSetObserver part and ListAdapter.registerDataSetObserver(DataSetObserver observer). DataSetObserver.onChanged() will be called by the BaseAdapter implementation, so it should work for all adapter.
BaseAdapter.notifyDataSetChanged():
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
You can use ArrayAdapter, which accepts Lists instead of Arrays. This way you can use notifyDataSetChanged() on it.
It seems that you think something wrong as I was did.
As you know, ListAdapter hasn't notifyDataSetChanged.
It seems that Cursor is the best match with a ListView. Doc. says that "Frequently that data comes from a Cursor, but that is not required. The ListView can display any data provided that it is wrapped in a ListAdapter."
If you check both links registerDataSetObserver ([DataSetObserver]2 observer), you can notice something. ListAdapter go well with a cursor. So why don't you try to use other adapters.
I think the best way to refresh adapter is using or extending BaseAdapter(with ListView). And use notifyDataSetChanged after change your dataset. In my case it works well and softly(invalidating list view and items, and scroll position is just there! Not going to first position).