I am making an android app and it allows user to enter the keywords in a editText and when they hit submit the recyclerview down below will show the result from an API request.
I have a updateList() method inside my recyclerView adapter class
list = savedInfo.getResult(); // get updated list from a singleton class
notifyDataSetChanged(); // update the recyclerView
I call this method after making the API Request successfully. However, it is now working, the recyclerView is not updated.
mSearchBox is the editText allows users to enter keywords and here is the onEditorAction, it will make an API call and if it called successfully, it will call UpdateList(), then the adapter will get the updated list and do notifyDataSetChanged()
mSearchBox.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) {
if (i == EditorInfo.IME_ACTION_DONE) {
HttpRequest httpRequest = new HttpRequest();
mHomeText.setText(textView.getText());
try {
if (httpRequest.makeCall(textView.getText().toString())){
adapter.updateList();
}
else {
// showing error message
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
return false;
}
});
Also, here is the steps to set my adapter
final ResultListAdapteradapter = new ResultListAdapter();
mResult.setAdapter(adapter);
mResult.setLayoutManager(new LinearLayoutManager(getContext()));
Debugging Steps: I tried to set break points and found out the API Request and my Singleton class are both working, the issue is just the RecyclerView.
Thank you so much!
When you do
list = savedInfo.getResult();
notifyDataSetChanged();
It is every time creating new list instance and the old one is not referenced anywhere. So instead of assigning to list you should do
list.clear()
list.addAll(savedInfo.getResult());
notifyDataSetChanged();
Don't forget to initialize list if not done before.
Related
I have this fragment in which I have a switch. What I want is that, when the switch's state is set to false, the items in the recycler view I have change color. The thing is, I am not sure how to pass the boolean from my fragment to my adapter. I was trying to do so through an interface but it doesn't seem to be working, the color is changed all the time, no matter the state of the switch.
This is the part in the fragment:
private Boolean switchState = false;
#Override
public boolean getClicked() {
if(switchState) {
return false;
}
return true;
}
The part in the adapter (after passing the interface to the class and everything):
interface ClickHere {
val clicked: Boolean
}
//inside the viewholder that has the bind function
if(clickHere.clicked){
faceImage.alpha = 0.5F
faceName.setTextColor(resources.getColor(R.color.disabled))
} else {
faceImage.alpha = 1F
faceName.setTextColor(resources.getColor(R.color.content_1))
}
So far, the recycler seems to be functioning as if the clickHere.clicked is always true, no matter the switch. How can I make it listen to the boolean from my fragment?
Alternative with variable as suggested in comment but still not working:
Inside ViewHolder class and binding function.
var adapterVariable = myAdapter(listener,displayMetrics)
if(adapterVariable.colored{
faceImage.alpha = 0.5F
faceName.setTextColor(resources.getColor(R.color.disabled))
} else {
faceImage.alpha = 1F
faceName.setTextColor(resources.getColor(R.color.content_1))
}
In fragment:
myAdapter adapter = new myAdapter(this, getContext().getResources().getDisplayMetrics());
binding.facialSwitch.setOnClickListener(view -> {
if(switchState){
turnSwitchOff();
adapter.setColored(switchState);
} else {
turnSwitchOn();
adapter.setColored(false);
}
});
Inside of your adapter, you can instantiate a private boolean variable with a matching getter and setter function. This variable is then used inside your viewhholder creation. Make sure, that you're calling notifyDataSetChanged in your setter method. Otherwise, your items will not get redrawn and therefore will not update their color.
You can do that with:
var colored = false
set(value) {
field = value
this.notifyDataSetChanged()
}
After adding these, add a listener to your switch, which calls the adapters new set function. Make sure you save your adapter as a variable inside your fragment.
This is achieved with:
var adapter: MyAdapter // create in onCreateView, save in your fragment
var switch: Switch // get from your UI
switch.setOnCheckedChangeListener(CompoundButton.OnCheckedChangeListener{ _, isChecked ->
adapter.colored = isChecked})
By that, there is no interface needed.
I have created a recyclerView -
public class PlayerListRecyclerViewAdapter extends RecyclerView.Adapter<PlayerListRecyclerViewAdapter.ViewHolder>
playerAdapter = new PlayerListRecyclerViewAdapter(this,cursor, playerDataSet);
playerRecyclerView.setAdapter(playerAdapter);
It all works fine, but I created the following method:
public void swapCursor(Cursor newCursor) {
// Always close the previous mCursor first
if (pCursor != null) pCursor.close();
pCursor = newCursor;
if (newCursor != null) {
// Force the RecyclerView to refresh
this.notifyDataSetChanged();
}
}
But when I try to call it from the activity that is linked to the adapter, it does not find it:
new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0,ItemTouchHelper.RIGHT) {
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
//Do nothing, we are not moving anything
//But maybe we can let the user arrange their list?
return false;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
long id = (long) viewHolder.itemView.getTag();
removePlayer(id);
playerAdapter.swapCursor(getAllPlayers());
}
}).attachToRecyclerView(playerRecyclerView);
}
Swap cusor is red and in the adapter it shows the message it is never used.
I am trying to follow:
https://github.com/udacity/ud851-Exercises/blob/student/Lesson07-Waitlist/T07.05-Solution-AddGuests/app/src/main/java/com/example/android/waitlist/GuestListAdapter.java
But I am not sure what I am missing?
Everything else works fine, I can swipe, it loads the details. All good just calling this method for some reason does not work.
please check your player adapter is getting null value.
because swapCursor method access after defining playeradapter object.
if(playerAdapter!=null){
playerAdapter.swapCursor(getAllPlayers());
}
Check your code for declaration of playerAdapter.
It should be as :-
PlayerListRecyclerViewAdapter playerAdapter;
InsteadOf
RecyclerView.Adapter playerAdapter;
I have created my own custom adapter class in my android app and I am calling it from one of my activity. I am adding some elements to the view from the adapter class and I need to access those variables from my activity class.
Now, ideally I would expect it to fill the view and then execute the further code in my activity class, but adapter class is taking some time to populate the view and in the meanwhile further code in my activity class is getting executed where no such elements have been added yet.
How do I handle this situation? I come from a js background. Do we have something like promises in java?
According to the answers I have my changed my code to this:
public class HomeActivity extends Activity {
GridView grid;
String text[] = {"Calendar","Uber","Weather","News","Youtube","Clock","Email","Maps","Twitter","Facebook"};
String list_app_name[] = {"calendar","uber","weather","news","youtube","clock","email","maps","twitter","facebook"};
String id_button[] = {"button_calendar","button_uber","button_weather","button_news","button_youtube","button_clock","button_email","button_maps","button_twitter","button_facebook"};
int image[] = {R.drawable.social_icons1,R.drawable.social_icons2,R.drawable.social_icons3,R.drawable.social_icons4,
R.drawable.social_icons5,R.drawable.social_icons6, R.drawable.social_icons7,R.drawable.social_icons8,
R.drawable.social_icons9,R.drawable.social_icons10};
private DrawerLayout mDrawerLayout;
private ActionBarDrawerToggle mDrawerToggle;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_home);
//setting up the adapter for gridView
grid = (GridView)findViewById(R.id.simpleGrid);
ImageAdapter ia = new ImageAdapter(this,image,text,id_button);
grid.setAdapter(ia);
ia.notifyDataSetChanged();
try {
initStateOfApps();
} catch (JSONException e) {
e.printStackTrace();
}
}
public void initStateOfApps() throws JSONException {
Log.d("here","here");
ArrayList<String> list = getEnabledApps();
Log.d("apps",list.toString());
for(int i=0;i<list.size();i++) {
String app_name = list.get(i);
ToggleButton button=null;
if(app_name.equals("calendar")) {
button = (ToggleButton)findViewById(R.id.button_calendar);
button.setChecked(true);
}
}
}
}
So what is happening is that I am creating some toggle buttons that are getting populated in the ImageAdapter class that I wrote.
Once the ImageAdapter is called, I call the notifydatasetchanged() on the adapter in order to update the view.
What I am doing inside the adapter is giving each of the toggle buttons some custom ID I wrote in res/values/ids.xml.
After using setId on each of the toggle buttons, I try using that ID in my activity class but it gives me nullPointerException in the initStateOfApps() where I am trying to change the state of button.
So even after using the notifyDataSetChanged it is still behaving the same.
ImageAdapter.java
public class ImageAdapter extends BaseAdapter {
private Context context;
private final int item_image[];
private final String item_text[];
private final String button_id[];
public ImageAdapter(Context context, int item_image[], String[] item_text,String[] button) {
this.context = context;
this.item_image = item_image;
this.item_text = item_text;
this.button_id = button;
}
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View gridView;
if (convertView == null) {
gridView = new View(context);
// get layout from custom_gridview.xml
gridView = inflater.inflate(R.layout.item, null);
// set value into imageview
final ImageView image = (ImageView) gridView.findViewById(R.id.item_image);
image.setImageResource(item_image[position]);
// set value into textview
TextView text = (TextView) gridView.findViewById(R.id.item_text);
text.setText(item_text[position]);
final ToggleButton button_ = (ToggleButton) gridView.findViewById(R.id.item_button);
if(position==0) {
button_.setId(R.id.button_calendar);
image.setId(R.id.image_calendar);
}
button_.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener()
{
#Override
public void onCheckedChanged(CompoundButton toggleButton, boolean isChecked)
{
if(context.getResources().getResourceEntryName(toggleButton.getId()).equals("button_calendar")) {
if(isChecked) {
try {
setStateOfApp("calendar","true");
} catch (JSONException e) {
e.printStackTrace();
}
Intent intent = new Intent(context, GoogleApp.class);
((Activity) context).startActivityForResult(intent,10);
} else {
try {
setStateOfApp("calendar","false");
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
}
} else {
gridView = (View) convertView;
}
return gridView;
}
}
You are trying to access View which is not a part of Activity's content view. So you can't access that view directly.
button = (ToggleButton)findViewById(R.id.button_calendar); // will return null
This ToggleButton will be null because findViewById will fail to find out ToggleButton in current content view because that view is present in your Adapter not in content view.
And you are getting nullpointerException because you are trying to access property on null view.
button.setChecked(true); // This button is null
In java we have <Future>, but I don't think it's what you're looking for.
The adapter (extending BaseAdaper) behaviour lets you create the adapter and, even in a second moment, change underlying data via getAdapter().setData() or whatever method you choose to add.
From this perspective, the adapter is a "stupid" component acting as A View containers, you should retrieve data elsewhere (CursorAdapter is different).
So, in your Activity, fill the adapter with needed data and, when finished, call adapter.notifyDatasetChanged(). This will inform the adapter that its own data has changed and it must refresh views
Yes, ideally, the population of the adapter should be coming from the outside. The adapter should really just take in a list of data and map that data to the views. For example, some method or task in the Activity could produce a list of data (probably asynchronously...since you mentioned it) that you then pass into the adapter and then you can notifyDataSetChanged() if you need to.
I can't see your code, but if for some reason the data is truly required to be populated from inside the adapter, you could use an event bus and subscribe to it in the Activity. I would recommend going with the first option, but here are some links if you choose to use an event bus:
https://github.com/greenrobot/EventBus
http://square.github.io/otto/
As per my understanding with your question
You are not properly managed the adapter data in your activity.
If any of the data or code interlinked with your adapter data or values
Then you can start those code after you retrieve the values or data and update the view in your activity.
Please note that use Viewholders in adapter to avoid slow populating and scrolling in listviews.
Viewholders will smooth your process.
I personally suggest you that
Please go with Recyclerview and RecyclerViewAdapter.
So many Android developers are using it.
If you have background tasks in adapter you can prefer to use RX Java or EventBus
If you provide the code
It's better for us to suggest exact solution
I have a ListView and in every single ListviewItem there is an ImageView with a little star (for marking it as favourite). Therefore I put an OnClickListener to the ImageView on every item in the custom ArrayAdapter.
imgStar.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Bitmap bitmap = ((BitmapDrawable)imgStar.getDrawable()).getBitmap();
Bitmap bitmap2 = ((BitmapDrawable)(context.getResources().getDrawable(R.drawable.ic_action_not_important))).getBitmap();
if(bitmap != bitmap2) {
imgStar.setImageResource(R.drawable.ic_action_not_important);
} else {
imgStar.setImageResource(R.drawable.ic_action_important);
}
}
});
The problem: When I get some items and click for example on the star of the first item, the image changes as it should but a few items lower the image changes too o.O
I tested it with some code: The thing that won't get into my head is it is only changing the image (on the other item below), code that would be executed in the onclick is only executed for the item I really click not for the one where the image changes too.
Why does the image of a random other item in the list change also? I hope someone can help me.
Custom Adapter Constructore Code
public LinkArrayAdapter(Context con, int textViewResourceId) {
super(con, textViewResourceId);
context = con;
}
The main problem is that you can't change the image of items in the onClick then leave it and hope it will be updated on every item on the list. Because onClick get called in different time than getView. So you must set item images outside of onClick but in the getView so every time that getView called for a specific item it will set the appropriate image for that item.
Define a boolean array in your CustomAdapter class as:
private boolean[] stars;
Then in constructor method of your class, initialize it as:
this.stars = new boolean[items.size()];
In the onClick method:
// **Edited to apply image update at click**
stars[position] = !stars[position];
notifyDataSetInvalidated();
At last in the getView() method of custom adapter
(ensure this code is not in any other inner blocks):
if (stars[position])
imgStar.setImageResource(R.drawable.ic_action_important);
else
imgStar.setImageResource(R.drawable.ic_action_not_important);
private int selectedPositions ;
// /... your code.
convertView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
OneComment mComment = mlist.get(position);
mComment.isStart = !mComment.isStart;
if(mComment.isStar){
//set star image
} else{
do not set star image}
}
});
#semsamot's answer works, however
notifyDataSetInvalidated() causes the List to reload and goes to the first item.
Use notifyDataSetChanged() instead.
I have a problem to update a listview, but doesn't work. Gives the NullPointerException on ".SetAdapter", does anyone know what can be?
Complete code: http://pastebin.com/AZdMi4sc
//Get filter from another fragment, using Interface, works fine.
public void setFilter(String tag) {
getFeedFilter(tag);
}
//Get only itens
private void getFeedFilter(String filter) {
ParseQuery<ParseObject> query = ParseQuery.getQuery("FeedPost");
query.whereContains("Type", filter);
query.findInBackground(new FindCallback<ParseObject>() {
public void done(List<ParseObject> responseList, ParseException e) {
if (e == null) {
List<Feed> items = new ArrayList<Feed>();
for(int i = 0; i < responseList.size(); i++) {
items.add(new Feed(responseList.get(i).getString("objectId"),
responseList.get(i).getString("Title"));
}
adapter = new FeedAdapter(getActivity(), items);
adapter.notifyDataSetChanged();
listView.setAdapter(adapter); // The error occurs here.
} else {
System.out.println("error: "+ e);
}
}
});
}
Logcat log:
04-27 03:52:23.175 4415-4415/com.fatos.application.app E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.NullPointerException
at com.fatos.application.app.fragment.FeedFragment$6.done(FeedFragment.java:256)
at com.parse.FindCallback.internalDone(FindCallback.java:45)
at com.parse.FindCallback.internalDone(FindCallback.java:31)
at com.parse.Parse$5$1.run(Parse.java:853)
.....
1) you should check if the adapter isn't null.this will fix your crash.
if(adapter!=null)
listView.setAdapter(adapter);
2) you should check if the items returned from response are ok, your problem might be there.
3) your code mixed up, you should init your views and set listeners on one method,
I always call init in fragment - OnCreateView ...
4) The problem might be with your listView, when you are using custom view make sure you give the full location of the custom listview class as your view instead of ListView.
Also, if you just update your adapter, you should not re create your adapter every time. create method in your adapter which set the new data. and just call adapter.notifyDataSetChanged();
For example:
In your adapter code add this:
public void setData(ArrayList<object> newData) {
this.data = newData;
}
then, in your activity notify adapter :
if(items!=null && items.size()>0) {
if(adapter==null){
adapter = new FeedAdapter(getActivity(), items);
}
else {
adapter.setData(yourArrayList);
adapter.notifyDataSetChanged();
}
else {
make toast for us or log to notice items are null...
}
if(adapter!=null)
listView.setAdapter(adapter);
I also think you should init your view and set listeners on your onCreateView method,
because everything is split in your code.