i got this problem yesterday:
Here I have a list returned by server, and there is a Hashmap list(List) and a Hashmap inside the list.And I got two situations, one is when the hashmap list is null, I add a new item into it,and then call notifyDataSetChanged() doesn't work, and the other situation is when i changed the value in hashmap, and notifyDataSetChanged(), it doesn't refresh either.And I'm sure the data has been changed successfully.
it shows in A activity and changed data in B activity.
A activity:
protected List<CommentEntity> mDataList;
protected ListView mListView;
protected BaseListAdapter mAdapter;
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
int position = data.getIntExtra("position", -1);
if (position > -1) {
CommentEntity entity = (CommentEntity) data.getSerializableExtra(AppVars.Keys.EXTRA_ENTITY);
mDataList.set(position, entity); // I want this list update
mAdapter.notifyDataSetChanged();
} else {
// error
}
}
public class BaseListAdapter extends BaseAdapter {
#Override
public int getCount() {
return mDataList == null ? 0 : mDataList.size();
}
#Override
public Object getItem(int position) {
return mDataList == null ? null : mDataList.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// actually this adapter is written in MyBaseFragment, and the child
// Fragment will override setupItemView() method.it's working fine
return setupItemView(position, convertView, parent);
}
}
B activity:
private CommentEntity mCommentEntity;
// the list below is inside CommentEntity, QuoteEntity is extends HashMap<>.
private List<QuoteEntity> mQuoteList;
private QuoteAdapter mAdapter;
public void update(QuoteEntity newQuote) {
mQuoteList.add(newQuote);
mCommentEntity.setReplyRows(mQuoteList);
mAdapter.notifyDataSetChanged();// This adapter is just working fine.
setResult(-1, getIntent().putExtra(AppVars.Keys.EXTRA_ENTITY, mCommentEntity));
}
Try resetting listview instead of using notifyDataSetChanged();
ListView lv = (ListView)FindViewById(R.id.lv);
BaseListAdapter aa = new BaseListAdapter(//constructor's parameters);
lv.setAdapter(aa);
Related
I have three fragments switched using a ViewPager and the third one contains a ListView.
My problem is that the ListView is not populated on the first time that I switch to that fragment. I have to switch back to the first then back to the third again and only will the ListView be populated. What seems to be the problem here?
How does ViewPager works anyways?
Here is my TabAdapter
public class TabAdapter extends FragmentStatePagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
TabAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#Nullable
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
}
Here is ListAdapter for the listView
public class ListAdapter extends BaseAdapter {
private ArrayList data = new ArrayList();
public ListAdapter(LinkedHashMap<String, ArrayList<String>> lhm) {
try {
data.addAll(lhm.entrySet());
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public int getCount() {
return data.size();
}
#Override
public LinkedHashMap.Entry<String, ArrayList<String>> getItem(int position) {
return (LinkedHashMap.Entry) data.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
#NonNull
#Override
public View getView(final int position, #Nullable View convertView, #NonNull ViewGroup parent) {
final LinkedHashMap.Entry<String, ArrayList<String>> item = getItem(position);
final ArrayList quotesList = item.getValue();
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.archive_list, parent, false);
}
TableRow tableRow = convertView.findViewById(R.id.container);
TextView title = convertView.findViewById(R.id.title);
ImageView copy = convertView.findViewById(R.id.copy);
copy.setVisibility(View.INVISIBLE);
title.setText(item.getKey() + " Quotes");
tableRow.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(getContext(), ViewActivity.class);
intent.putExtra("title", item.getKey());
intent.putStringArrayListExtra("quotes", quotesList);
if (interstitialAd.isLoaded()) {
interstitialAd.show();
startActivity(intent);
} else {
startActivity(intent);
}
}
});
return convertView;
}
}
Update
The problem seems to be from the Linkedhashmap. I tried manually putting values into the Hashmap and it worked. The problem now is my current code. I populate my hashmap by retrieving data from firebase and that seems to be the problem? Pershaps it isn't populated as quickly?
private void initArchives() {
DatabaseReference dBase = FirebaseDatabase.getInstance().getReference().child("archive");
dBase.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for(DataSnapshot snapshot : dataSnapshot.getChildren()) {
DataPojo dp = snapshot.getValue(DataPojo.class);
hm.put(dp.getName(), dp.getMessages());
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_archive, container, false);
listView = view.findViewById(R.id.listView);
searchView = view.findViewById(R.id.searchView);
initArchives();
listAdapter = new ListAdapter(hm);
listView.setAdapter(listAdapter);
listView.invalidateViews();
loadAds();
return view;
}
First of all FragmentStatePagerAdapter is really a pain in the ass. But we have to work with that. I suggest you to Ctrl + click on FragmentStatePagerAdapter and read source code.
Another thing is adding fragments to your ArrayList with addFragment() method might not be the best idea since FragmentStatePagerAdapter is saving the states of the fragments and loads them from its own FragmentManager.
So the fragment you added might not be the fragment you are getting.
A possible solution to that would be using tags.
There are some must read articles about the topic here here and here.
And oh one other thing. ViewPager creates three fragments at a time when using FragmentStatePagerAdapter. First it creates fragment to display and next the fragment on the right and last the fragment on the left. Since you have three fragments, the last (3rd) fragment is not created right away (There is no fragment on the left of the first fragment). Maybe you are loading some data that takes time to download on the third fragment? And next time you display the 3rd fragment it loads from the saved state of FragmentStatePagerAdapter. These are just some possibilities to think about. Not necessarily is the right solution. Good luck.
I try to remove a specific item from a listView but it's always remove the last item.
I create a custom adapter to my listview.
I try to search for a solution and i found some posts about this problem but I still didn't success to solve the problem
custom adapter below:
public class ListViewAdapter extends BaseAdapter{
public ArrayList<HashMap<String, String>> list;
public static final String WORD_COLUMN="First";
public static final String TRAN_COLUMN="Second";
Activity activity;
TextView txtFirst;
TextView txtSecond;
public ListViewAdapter(Activity activity,ArrayList<HashMap<String, String>> list){
super();
this.activity=activity;
this.list=list;
}
#Override
public int getCount() {
return list.size();
}
#Override
public Object getItem(int position) {
return list.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater=activity.getLayoutInflater();
if(convertView == null){
convertView=inflater.inflate(R.layout.column_row, null);
txtFirst=(TextView) convertView.findViewById(R.id.wordColumn);
txtSecond=(TextView) convertView.findViewById(R.id.tranColumn);
}
HashMap<String, String> map=list.get(position);
txtFirst.setText(map.get(WORD_COLUMN));
txtSecond.setText(map.get(TRAN_COLUMN));
return convertView;
}
}
activity code below:
public class MainActivity extends AppCompatActivity{
private ListView lv;
private ArrayList<HashMap<String, String>> hashList;
private ListViewAdapter adapter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv = (ListView)findViewById(R.id.gvWords);
hashList = new ArrayList<HashMap<String, String>>();
adapter=new ListViewAdapter(this, hashList);
lv.setAdapter(adapter);
onClickButtons();
}
public void onClickButtons()
{
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
hashList.remove(i);
adapter.notifyDataSetChanged();
}
});
}
Thank's :)
You can try to access and remove clicked object via getItemAtPosition(position) and then remove it from ArrayList via .remove(Object o)
Your listener will therefore look like this:
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
hashList.remove(lv.getItemAtPosition(i));
adapter.notifyDataSetChanged();
}
});
This approach is mentioned in official doc: https://developer.android.com/reference/android/widget/AdapterView.OnItemClickListener.html
Implementers can call getItemAtPosition(position) if they need to access the data associated with the selected item.
after refreshing it's remove the right item
You've cached the two TextViews, which aren't updated as part of notifying the adapter.
Alter the convertView check and remove those fields, then lookup how to implement the ViewHolder pattern (or use a RecyclerView)
I found this problem,
and removed the if(convertView == null){ It's working,
Please let us know for this reason.
I really need some good hint.... i have been hours debugging this (((...
public class Travel {
public static String from, to;
Travel(String f, String t) {
this.from = f;
this.to = t;
}
}
final class NotesAdapter extendes ArrayAdapter<Travel>{
private final List<Travel> myC;
public NotesAdapter(Context context, int resource, List<Travel> myCt) {
super(context, resource, myCt);
this.myC = myCt;
}
#Override
public int getCount() {
return myC.size();
}
#Override
public Travel getItem(int position) {
return myC.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup adapterView) {
Travel trv = myC.get(position);
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.my_lists, adapterView, false);
}
TextView from = (TextView)convertView.findViewById(R.id.tv_from);
TextView to = (TextView)convertView.findViewById(R.id.tv_to);
from.setText(trv.from);//.getMFrom()
to.setText(trv.to);//.getMTo()
return convertView;
}
In NotesAdapter i have implemented only the method you see here.
Then in the main activity i launch startactivityforresult and in the onactivityresults i add elements to the List object.
List<Travel> myCities = new ArrayList<Travel>();
the List object is initialized as an ArrayList (...new ArrayList<>()...).
I also have:
mList = (ListView)findViewById(R.id.for_lists);
na = new NotesAdapter(this,R.layout.my_lists,myCities);
So:
nCity = data.getStringExtra("content");
to collect the new string
myCities.add(new Travel("Home Town",nCity));
to add a new travel.... and:
na.notifyDataSetChanged();
mList.setAdapter(na);
this is the all code:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
if(requestCode == MY_REQUEST_CODE){
if(resultCode == RESULT_OK){
nCity = data.getStringExtra("content");
if (!myCities.contains(nCity)) {
myCities.add(new Travel("Home Town",nCity));
na.notifyDataSetChanged();
} else {
Toast.makeText(this,"This City has already been added",Toast.LENGTH_LONG).show();
}
}
else {
//do something when user refused to complete action
}
hT.setText("Home Town");
iB.setText(infoBar);
na.notifyDataSetChanged();
mList.setAdapter(na);
mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
}
});
}
else {
// not my business another request
}
}
When i use just a List of String all works perfectly.
The result with a List of Travel is that all views in the listview show the last added travel.
For layout files i use basic implementations.
Please help... ^_^
na is never set to List until you call mList.setAdapter(na);. You don't need to call na.notifyDataSetChanged();
Please add one new method in NotesAdapter adapter. This method will update data:
public void updateData(List<Travel> myCt){
this.myC = myCt;
}
then update your code
na.notifyDataSetChanged();
mList.setAdapter(na);
with
na.updateData(myCities);
mList.setAdapter(na);
The problem was static
public static String from, to
Basically, I'm trying to programatically find out how many items are going to fit in a RecyclerView (and be visible to user, of course) in order to determine how many of them to fetch from cache.
I'm using a LinearLayoutManager.
Also, I'm aware of the LinearLayoutManager method findLastVisibleItemPosition, but obviously it's useless in this case since we're talking on before-initialization time, not after (so it returns -1).
Tried reading the docs or thinking on a creative but efficient idea, but I got nothing on my mind.
Any ideas?
This sounds actually pretty interesting, but only works if your height (vertical scroll) or width (horizontal scroll) is fixed, meaning no wrap_content.
No sample code and nothing tested here:
Create an Adapter with a setter for getCount that gets returned in case your data-source is null/empty
in getCount return at least 1 if your data is empty/null
make sure onBindViewHolder() can handle empty/non-existent data
add a OnChildAttachStateChangeListener to your RecyclerView, everytime the listener gets called, use the view to view.post(new Runnable() {...increase adapters getCount...adapter.notifyItemInserted()} (that runnable is necessary to avoid crash+burn)
OnChildAttachStateChangeListener gets called again >>> compare getCount and findLastVisibleItemPosition. If getCount > findLastVisibleItemPosition + 1 remove that listener. The number of fixed-size views fitting into ListView is findLastVisibleItemPosition + 1
Get your data and set it into you adapter, call notifyDataSetChanged
make sure getCount returns the data-source length from now on.
you could hide the listview behind a loadingscreen, or you can set the child views to invisible in onBindViewHolder
EDIT:
Create an Adapter which returns a ridiculous high count when no data is set and make sure it handles missing data correctly in onBindViewHolder
Extend LinearLayoutManager and Override onLayoutChildren() after the super call if getItemCount() > getChildCount() getChildCount() is the number of Views that would be visible in your RecyclerView
MainActivity.class
public class MainActivity extends AppCompatActivity {
private PreCountingAdapter mAdapter;
private RecyclerView mRecyclerView;
private PreCountLinearLayoutManager mPreCountLayoutManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
mPreCountLayoutManager = new PreCountLinearLayoutManager(this,
LinearLayoutManager.VERTICAL, false);
mPreCountLayoutManager.setListener(new PreCountLinearLayoutManager.OnPreCountedListener() {
#Override
public void onPreCounted(int count) {
mPreCountLayoutManager.setListener(null);
loadData(count);
}
});
mRecyclerView.setLayoutManager(mPreCountLayoutManager);
mAdapter = new PreCountingAdapter();
mRecyclerView.setAdapter(mAdapter);
}
private void loadData(final int visibleItemCount) {
// load data here, probably asynchronously,
// for simplicity just an String Array with size visibleItemCount
final List<String> data = new ArrayList<>();
for (int i = 0; i < visibleItemCount; i++) {
data.add(String.format("child number #%d", i));
}
mRecyclerView.post(new Runnable() {
#Override
public void run() {
mAdapter.swapData(data);
}
});
}
#Override
protected void onDestroy() {
mPreCountLayoutManager.setListener(null);
super.onDestroy();
}
}
PreCountLinearLayoutManager.class
public class PreCountLinearLayoutManager extends LinearLayoutManager {
private OnPreCountedListener mListener;
public interface OnPreCountedListener {
void onPreCounted(int count);
}
public PreCountLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
#Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
super.onLayoutChildren(recycler, state);
if (getItemCount() > getChildCount()) {
if (mListener != null) {
mListener.onPreCounted(getChildCount());
}
}
}
public void setListener(OnPreCountedListener listener) {
mListener = listener;
}
}
PreCountingAdapter.class
public class PreCountingAdapter extends RecyclerView.Adapter<PreCountingAdapter.ViewHolder> {
private List<String> mData;
public void swapData(List<String> data) {
mData = data;
notifyDataSetChanged();
}
public class ViewHolder extends RecyclerView.ViewHolder {
View mItemView;
TextView mTextView;
public ViewHolder(View itemView) {
super(itemView);
mTextView = (TextView) itemView.findViewById(R.id.text_view);
mItemView = itemView;
}
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
return new ViewHolder(inflater.inflate(R.layout.recycler_child, parent, false));
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
if (mData == null) {
// we are in precounting stage
holder.mItemView.setVisibility(View.INVISIBLE);
} else {
String item = mData.get(position);
holder.mItemView.setVisibility(View.VISIBLE);
holder.mTextView.setText(item);
}
}
#Override
public int getItemCount() {
return mData == null ? Integer.MAX_VALUE : mData.size();
}
}
I'm trying to inflate a list using baseadapter within an activity. The list just doesn't inflate. From the logs implemented within the class, the getView() function doesn't even execute. Here's the code. -
public class CallLog extends Activity {
ListView logList;
List mList;
Context mCtx;
ArrayList<String> logName;
ArrayList<String> logNumber;
ArrayList<String> logTime;
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.reject_call_log);
mCtx = getApplicationContext();
ListView logList = (ListView) findViewById(R.id.log_list);
mList = new List(mCtx, R.layout.log_row);
logList.setAdapter(mList);
SharedPreferences savedLogName = PreferenceManager.getDefaultSharedPreferences(mCtx);
SharedPreferences savedLogNumber = PreferenceManager.getDefaultSharedPreferences(mCtx);
SharedPreferences savedLogTime = PreferenceManager.getDefaultSharedPreferences(mCtx);
try{
logName = new ArrayList(Arrays.asList(TextUtils.split(savedLogName.getString("logName", null), ",")));
logNumber = new ArrayList(Arrays.asList(TextUtils.split(savedLogNumber.getString("logNumber", null), ",")));
logTime = new ArrayList(Arrays.asList(TextUtils.split(savedLogTime.getString("logTime", null), ",")));
Collections.reverse(logName);
Collections.reverse(logNumber);
Collections.reverse(logTime);
}catch(NullPointerException e){
e.printStackTrace();
//TextView noLog = (TextView)findViewById(R.id.no_log);
}
}
public class List extends BaseAdapter {
LayoutInflater mInflater;
TextView nameText;
TextView numberText;
TextView timeText;
int timePos = 1;
public List(Context context, int resource) {
}
#Override
public int getCount() {
return 0;
}
#Override
public Object getItem(int i) {
return null;
}
#Override
public long getItemId(int i) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (convertView == null) {
v = mInflater.inflate(R.layout.row, null);
}
nameText = (TextView) v.findViewById(R.id.log_name);
numberText = (TextView) v.findViewById(R.id.log_number);
timeText = (TextView) v.findViewById(R.id.log_time);
nameText.setText(logName.get(position));
numberText.setText(logNumber.get(position));
timeText.setText(logTime.get(timePos) + logTime.get(timePos+1));
Log.d("RejectCall", "ListView");
timePos+=2;
return v;
}
}
}
Where is it all going wrong? Also, is there a better way to do what I'm trying to do?
Please replace the following code :
#Override
public int getCount() {
return 0;
}
with
#Override
public int getCount() {
return logName.size();
}
As list view only show the numbers of rows that is returned by this method and right now you are returning 0;
And after fetching the data in arraylist please use adapter.notifyDataSetChanged() to notify the list view.
You have to call notifyDataSetChanged() as you are filling data in array list after setting the adapter. so to notify the list view that data has been changed you have to call notify method(as above)
Your getItem() and getCount() haven't been implemented. If you want any kind of adapter to work for the list, these need to be implemented. Your list is also not holding any actual data, so getItem() has nothing to set.
Don't forget to call notifiyDataSetChanged() in your adapter after you set appropriate implementations for the above two functions.