I have two classes , the first one is for the GUI , where I declared my listview and the adapter , and the setters , to call them from my second class .
public class AndroidGUIModifier implements IMyComponentGUIModifier, IFragmentEvents {
private transient ListView lv;
List<String> mydeviceslist;
ArrayAdapter<String> adapter ;
public void setAdapter(ArrayAdapter<String> adapter) {
this.adapter = adapter;
adapter.notifyDataSetChanged();
}
public void setMydeviceslist(List<String> mydeviceslist) {
this.mydeviceslist = mydeviceslist;
}
#Override
public void onCreateView() {
lv=(ListView) fragment.findViewById("xdevices") ;
mydeviceslist = new ArrayList<String>();
adapter = new ArrayAdapter<String>(fragment.getContext(),android.R.layout.simple_list_item_1,mydeviceslist);
lv.setAdapter(adapter);
In my second class I'll wait an event to receive the list that I want to load it in my listview , then I'll call the list setter to set the new received list and the adapter setter to update it , but it didn't work , nothing was displayed despite I receieved the list of devices in my log .
public class triprincipal extends BCModel {
public List<String> mydevices ;
BCEvent bcEvent;
final ArrayAdapter<String> adapter =guiModifier.getAdapter();
while (isRunning()) {
bcEvent = waitForBCEvent();
if (bcEvent.getID() == checkevent) {
mydevices = bcCommandSenderPlugin.getDevicesNames(); // here I get a list of my devices
Log.i("devices", mydevices.toString());
guiModifier.getFragment().getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
guiModifier.setMydeviceslist(mydevices);
guiModifier.setAdapter(adapter);
}
} );
In setMydeviceslist() do it like:
this.mydeviceslist.addAll(mydeviceslist);
adapter.notifyDataSetChanged();
Hope it will help you out.
adapter.notifyDataSetChanged() will not work in this case, as the value of the reference you have passed to the adapter doesn't actually change.
You will need to create a new Adapter and set it to the ListView to make it work. Change your setAdapter() to this :
public void setAdapter() {
this.adapter = new ArrayAdapter<String>(fragment.getContext(), android.R.layout.simple_list_item_1, mydeviceslist);
lv.setAdapter(adapter);
}
try to update the list in same fragment/activity and after update call notifyDataSetChanged() both in same activity/fragment ....donot set adapter on list repetedly......hope it helps
Related
I am learning RecyclerView in MVVM pattern from a youtube video. I create a recycler view to load very simple items. It works fine, but when I navigate to a new activity and then come back to the activity using Recycler View. My items in the list is duplicated. For example, my recycler view shows 2 items like Item1 and Item2. After I move to a new activity and return back, the list become Item1, Item 2, Item1 and Item2. So, each time I move to the new activity and return back, it keep doubling more and more. I only want the recycler view load one time, how can I solve this problem? Thank you.
My repo:
public class DWCategoryRepository {
private static DWCategoryRepository instance;
private ArrayList<DWCategories> dataSet = new ArrayList<>();
public static DWCategoryRepository getInstance() {
if (instance == null){
instance = new DWCategoryRepository();
}
return instance;
}
public MutableLiveData<List<DWCategories>> getDWCategories(){
setDWCategories();
MutableLiveData<List<DWCategories>> data = new MutableLiveData<>();
data.setValue(dataSet);
return data;
}
private void setDWCategories() {
dataSet.add(new DWCategories("Item1"));
dataSet.add(new DWCategories("Item2"));
}
}
My ViewModel:
public class MainWalletViewModel extends ViewModel {
private MutableLiveData<List<DWCategories>> mCategories;
private DWCategoryRepository mRepo;
public void init(){
if (mCategories != null) {
return;
}
mRepo = DWCategoryRepository.getInstance();
mCategories = mRepo.getDWCategories();
}
public LiveData<List<DWCategories>> getDWCategories(){
return mCategories;
}
}
View:
public class MainWalletActivity extends DWBaseActivity implements WalletCategoryAdapter.OnCategoryListener {
private WalletCategoryAdapter mWalletCategoryAdapter;
private MainWalletViewModel mMainWalletViewModel;
private RecyclerView mRecyclerView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initializeDataBinding();
}
private void initializeDataBinding() {
MainWalletActivityBinding dataBinding = DataBindingUtil.setContentView(this, R.layout.main_wallet_activity);
setSupportActionBar(dataBinding.walletToolbar);
//Enable Back button on Toolbar
showBackArrowOnToolbar();
//Get Categories from View Model
initCategories();
//Set up adapter
mWalletCategoryAdapter = new WalletCategoryAdapter(this, mMainWalletViewModel.getDWCategories().getValue(), this);
//Set adapter to Recycler view
mRecyclerView = dataBinding.walletCategoryRV;
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setAdapter(mWalletCategoryAdapter);
//Add divider to Recycler view
mRecyclerView.addItemDecoration(new DividerItemDecoration(MainWalletActivity.this,
DividerItemDecoration.VERTICAL));
}
private void initCategories(){
mMainWalletViewModel = ViewModelProviders.of(this).get(MainWalletViewModel.class);
mMainWalletViewModel.init();
mMainWalletViewModel.getDWCategories().observe(this, new Observer<List<DWCategories>>() {
#Override
public void onChanged(#Nullable List<DWCategories> dwCategories) {
mWalletCategoryAdapter.notifyDataSetChanged();
}
});
}
}
You are calling setDWCategories in your getter.
public MutableLiveData<List<DWCategories>> getDWCategories(){
setDWCategories(); // <- Remove this line!
MutableLiveData<List<DWCategories>> data = new MutableLiveData<>();
data.setValue(dataSet);
return data;
}
You should only initialize your repo data once. Maybe do it in your getInstance() method if you are ok, starting over every time you run the app.
I have an custom adapter that is giving me problem. When I add or edit something it updates the listview but when I delete it doesn't.
Code when I remove something:
private ArrayList<Teams> m_orders = null;
private TeamsAdapter m_adapter;
private ListView lstv;
...
private void deleteTeam(int indexRemove){
hasKeys.remove(hasKeys.indexOf(m_orders.get(indexRemove).getTeamName()));
Menu.teams.remove(m_orders.get(indexRemove).getTeamName());
m_orders.remove(indexRemove);
m_adapter.notifyDataSetChanged();
}
I tried use a Runnable, but without success.
private Runnable returnRes = new Runnable() {
#Override
public void run() {
if(m_orders != null && m_orders.size() > 0){
m_adapter.notifyDataSetChanged();
for(int i=0;i<m_orders.size();i++)
m_adapter.add(m_orders.get(i));
}
m_adapter.notifyDataSetChanged();
}
};
My onCreate method:
lstv = findViewById(R.id.teamsList);
m_orders = new ArrayList<Teams>();
this.m_adapter = new TeamsAdapter(this, R.layout.row, m_orders);
lstv.setAdapter(this.m_adapter);
You have to remove an item from the ArrayList which is inside the Adapter class. So you need to write your delete function inside the adapter class. In this way will able to delete items and update listview by calling notifyDataSetChanged() method.
You are creating an m_adapter with m_orders dataset, but you are removing item from m_teams dataset, make no sense, they are different instances.
final List<Teams> mTeams = new ArrayList<>();
TeamsAdapter mAdapter;
onCreate() {
mAdapter = new TeamsAdapter(this, R.layout.row, mTeams);
ListView listView = (ListView) findViewById(R.id.teamsList);
listView.setAdapter(mAdapter);
}
addTeams(Collection<Teams> items) {
mTeams.addAll(items);
mAdapter.notifyDataSetChanged();
}
deleteTeam(int index) {
mTeams.remove(index);
mAdapter.notifyDataSetChanged();
}
Please don't mark it as duplicate, as the solutions provided yesterday did not work.
I have populated a recyclerview, and it is working fine, albeit it loads a bit slowly.
However, say when I add a new name and update the recyclerview it crashes with the NullPointerException.
Below are the classes and code:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mainOnes);
recyclerView = (RecyclerView) findViewById(R.id.recycleOnes);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setHasFixedSize(true);
adapter = new MusicRecyclerAdapter(list);
recyclerView.setAdapter(adapter);
populateList();
}
public void populateList(){
DisplayUsersList displayUsersList = new DisplayUsersList();
displayUsersList.execute();
}
public class DisplayUsersList extends AsyncTask<String, String, String> {
#Override
protected String doInBackground(String... params) {
...
}
#Override
protected void onPostExecute(String postData) {
try {
list.clear();
JSONArray jsonArray = new JSONArray(postData);
for(int i=0; i < jsonArray.length(); i++){
String forename = jsonArray.getJSONObject(i).getString("forename");
String surname = jsonArray.getJSONObject(i).getString("surname");
UserDetails userDetails = new UserDetails(forename, musicName, surname);
list.add(userDetails);
}
adapter.notifyDataSetChanged();
} catch (JSONException e) {
e.printStackTrace();
}
}
}
public class AddUsers extends AsyncTask<String, String, String> {
#Override
protected String doInBackground(String... params) {
...
}
#Override
protected void onPostExecute(String postData) {
populateList();
}
}
I think it is crashing because of the populateList() method call because I have already initialised everything
My logcat:
java.lang.NullPointerException
at georgezs.userdas.UserDBGUI$DisplayUsersList.onPostExecute(UserDBGUI.java:218)
at android.os.AsyncTask.finish(AsyncTask.java:741)
at android.os.AsyncTask.access$600(AsyncTask.java:197)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:654)
at android.os.Handler.dispatchMessage(Handler.java:100)
Line 218 is adapter.notifyDataSetChanged();
I tried instantiating the recyclerview in onPostExecute but it keeps crashing. My code works in the sense that it actually loads the data and displays it in a recyclerview. It just doesn't redisplay or reload when I call the populateList() method again.
This bug has been bugging me in my sleep literally - so any help and solutions would mean a lot to me
EDIT: I THINK I KNOW EXACTLY WHERE THE ERROR IS - IT IS IN THE POSTEXECUTE METHOD IN ADDUSERS CLASS.
Thanks
Just check if the adapter is not null before calling notifyDataSetChanged():
if (adapter != null) {
adapter.notifyDataSetChanged();
}
This happens because the AsyncTask executes asynchronously and by the time it finishes, the adapter might not be available anymore.
One option you could try is, instead of using
adapter.notifyDataSetChanged();
replace with
recyclerView.getAdapter().notifyDataSetChanged();
This is assuming for whatever reason, you have got your adapter set to null, but not shown in the code clearly above, while your recyclerview is still fully intact.
p/s: I also didn't see you update your latest retrieved data and insert into the adapter. You might want to check that as well.
You're handling variables outside the scope of your main class in a Threaded class which can always be sketchy. Granted AsyncTask is supposed to synchronize operations for you, I'm always cautious
Try passing your list and adapter to your DisplayUsersList class e.g.
DisplayUsersList displayUsersList = new DisplayUsersList(list, adapter);
displayUsersList.execute();
And in your DisplayUsersList make sure the reference of these is explicit e.g.
public class DisplayUsersList extends AsyncTask<String, String, String> {
private final List<UserDetails> list;
private final Adapter adapter;
public DisplayUsersList(List<UserDetails> list, Adapter adapter) {
this.list = list;
this.adapter = adapter;
}
...
Also, try moving the populateList(); to OnStart rather than onCreate
As although the variables are assigned they may still be initialising.
Edit
Could you try moving the call to the top level class e.g.
public void dataSetChangedCallback() {
adapter.notifyDataSetChanged();
}
And in your DisplayUsersList change adapter.notifyDataSetChanged(); to
...
dataSetChangedCallback();
...
I need to somehow know when the adapter of a ListView instance changes.
I was thinking of adding an observer to the listView object, but it does not implement the Observable interface.
ListView listView;
listView.addObserver(...); // method does not exist!
Is there any other way I can know when the adapter of a list view changes? e.g. the setAdapter() method is called... ?
--- UPDATE ---
Here's an code example:
final ListAdapter firstAdapter = new SimpleAdapter(
this,
new ArrayList<Map<String, Object>>(),
android.R.layout.simple_list_item_1,
new String[] {"AAA"},
new int[] {android.R.id.text1}
);
final ListAdapter secondAdapter = new SimpleAdapter(
this,
new ArrayList<Map<String, Object>>(),
android.R.layout.simple_list_item_1,
new String[] {"AAA"},
new int[] {android.R.id.text1}
);
setListAdapter(firstAdapter);
(new Timer()).schedule(new TimerTask() {
#Override
public void run() {
Runnable r = new Runnable() {
#Override
public void run() {
setListAdapter(secondAdapter);
}
};
new Handler(Looper.getMainLooper()).post(r);
}
}, 1000);
My question is: How can the firstAdapter know when it is detached from being the listView's actual adapter (because secondAdapter replaces it)?
Is this what you are looking for? You can set an observer on the adapter.
registerDataSetObserver
Turns out the Adapter gets notified when it's deregistered from the ListView, so I am using this:
#Override
public void unregisterDataSetObserver(DataSetObserver observer) {
super.unregisterDataSetObserver(observer);
Log.d("Adapter just got unregistered from the listView!");
[...]
}
I have a public method in DatabaseHelper.java class like below:
public List<Presentation> getAllPresentations() {
List<Presentation> presentations = new ArrayList<Presentation>();
//
//
// some code
//
//
return presentations;
}
In my MainActivity.java I have added this lines:
btnLoad.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
List list = db.getAllPresentations();
ListView l;
l=(ListView)findViewById(R.id.list);
l.setAdapter(new ArrayAdapter<List>(this,R.layout.view_presentation, list));
}
});
BUT, something is wrong on the line:
l.setAdapter(new ArrayAdapter<List>(this,R.layout.view_presentation, list));
Can someone help me?
There are at least two problems:
In the context of an anonymous inner class (like new View.OnClickListener()), this refers to the instance of the inner class. The ArrayAdapter constructor needs a Context, so you must use MainActivity.this instead.
The type parameter of ArrayAdapter<T> must be the item type. So in this case, it should be ArrayAdapter<Presentation>.
So:
List<Presentation> list = db.getAllPresentations();
ListView l = (ListView)findViewById(R.id.list);
l.setAdapter(new ArrayAdapter<Presentation>(MainActivity.this, R.layout.view_presentation, list));