I want to create searching for items from recycler view.
I have MainActivity where I have the main recycler view and I have SearchActivity where I have the seconds recycler view for appearing of searching items. When user inputs a letter in the input I ask my SQLite for the same query as my input text. If my SQLite gives me data I insert these data in the second recycler view and appear their to user.
Here is it my code:
// onCreate() method
initRecyclerView(); // init empty recycler view
EditText et_input = getActivity().findViewById(R.id.et_search_for_task_input);
et_input.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged (CharSequence s, int start, int count, int after) {
// nothing...
}
#Override
public void onTextChanged (CharSequence s, int start, int before, int count) {
if (count == 0) { // if empty I show to user nothing
adapter.clearAll();
adapter.notifyDataSetChanged();
} else {
Cursor cursor = database.rawQuery("SELECT * FROM todo WHERE task LIKE '%" + String.valueOf(s) + "%'", null); // ask for data from database
if (cursor.moveToFirst()) { // if the response wasn't empty
do {
// add filter data in adapter and save adapter(notifyDataSetChanged();)
adapter.addTask(cursor.getString(cursor.getColumnIndex("task")));
adapter.notifyDataSetChanged();
} while (cursor.moveToNext());
}
cursor.close();
}
}
#Override
public void afterTextChanged (Editable s) {
// adapter.clearAll();
}
});
All works good. But I have the issue. When user inputs a letter he gets a list of searching items and if he want to add or remove the text in input he gets a list of new searching items with OLD SEARCHING ITEMS. So, I need to delete old searching items from adapter. How can I do it????
I've been trying to do the next: called in afterTextChanged the method adapter.clearAll();. I hoped that when user finishs to input his data, these data adds in adapter and adapter clears without update recycler view(notifyDataSet...();) and when he will search another he gets new list of searching items without old searching items. But I have nothing!!! Help me pls!
I hope I was able to tell you my problem and you can understand me :)
It's not very clear what exactly is your problem, but from what I think it is:
Every time user types a character a new character another onTextChanged event is fired and as you don't clear adapter in-between obviously your addTask entries accumulate.
afterTextChanged gets called after onTextChanged, so if you clear your adapter right after you populate it it ends up empty. I'm not sure if notifyDataSetChanged redraws RecyclerView immediately or queues it, but it's quite likely another notifyDataSetChanged might sneak in during adapter.clearAll or somewhere else leaving you with empty View. You should clear adapter immediately before repopulating it, not right after.
You don't need to call notifyDataSetChanged after every addTask. Add all the task for current iteration, then call notify... once.
If you read up on ontextChanged you'll see, that count in parameters only show how many characters changed (or rather how many characters are in currently typed word or smth), it's not the best choice to use it to detect if the CharSequence s is empty. Rather check the length of CharSequence s.
So what you need to do is:
#Override
public void onTextChanged (CharSequence s, int start, int before, int count) {
if (s.length() == 0) { // if empty I show to user nothing
adapter.clearAll();
adapter.notifyDataSetChanged();
} else {
Cursor cursor = database.rawQuery("SELECT * FROM todo WHERE task LIKE '%" + String.valueOf(s) + "%'", null); // ask for data from database
adapter.clearAll();
if (cursor.moveToFirst()) { // if the response wasn't empty
do {
// add filter data in adapter and save adapter(notifyDataSetChanged();)
adapter.addTask(cursor.getString(cursor.getColumnIndex("task")));
} while (cursor.moveToNext());
}
cursor.close();
adapter.notifyDataSetChanged();
}
}
That should do it provided your adapter is working as I expect it to. You never showed your RecyclerView adapter, so I don't know what addTask and clearAll do.
Related
As the title says, I need to make RecyclerView item number, change to that same number, which is in that same Activity (in real-time). So when the number in Activity changes, also that number which is in the RecyclerView item, should change to that same number.
When the game starts, par number and those numbers in RecyclerView are the same as they should but ...
Now the 2nd hole par number changes to 3, but those RecyclerView item numbers are still 4, but instead, they should be also 3. How Can I make this change happen?
Here is my adapter:
#Override
public void onBindViewHolder(#NonNull GameViewHolder holder, int position) {
GameItem currentItem = mGameList.get(position);
holder.mTextPlayer.setText(currentItem.getText1());
holder.mTextPar.setText(currentItem.getText2());
/** If persons par number is smaller than course par number, then change persons par number background to blue **/
if (Integer.parseInt(holder.mTextPar.getText().toString()) == 1) {
holder.mTextPar.setBackgroundResource(R.drawable.border_box_yellow);
} else if (Integer.parseInt(holder.mTextPar.getText().toString()) < Integer.parseInt(ActivityGame.mParNm.getText().toString())) {
holder.mTextPar.setBackgroundResource(R.drawable.border_box_blue);
} else if (Integer.parseInt(holder.mTextPar.getText().toString()) > Integer.parseInt(ActivityGame.mParNm.getText().toString())) {
holder.mTextPar.setBackgroundResource(R.drawable.border_box_red);
} else if (Integer.parseInt(holder.mTextPar.getText().toString()) == Integer.parseInt(ActivityGame.mParNm.getText().toString())) {
holder.mTextPar.setBackgroundResource(R.drawable.border_box_green);
}
Here in my GameAdapter, where I set those items background colors (according also to that par number) I tried to do:
holder.mTextPar.setText(ActivityGame.mParNm.getText().toString());
Meaning that I tried to set the number according to Par number, but it didn't change at all, it just changed the background of the items, but number stayed the same.
Do two simple things.
Create a method in the adapter-
public void updateAdapterData() {
notifyDataSetChanged();
}
Next, add the below method in the activity and call it when there is a change in the par value in your activity.
private void updateGameArrayList(int parVal) {
for(GameItem model : GameList) {
model.setText2(parVal);
adapterVariableInActivity.updateAdapterData();
}
}
These line of code worked for me.
UPDATE THE DATA: The only things I had to do is
myAdapter.notifyDataSetChanged();
1- You have to implement this in your adapter.
2- You had to do all of this in the Actvity/Fragment code not in the RecyclerView Adapter code.
I hope it helps you!
You have to add a integer field in GameList Model that you fix at the time of adapter as same as activity value.
class GameList{
String testPar;
}
then if the text is changed in Activity you have to change the GameList objects of all List to current text of activity and call
// index in which you want to change text in list
gameList.get(index).setTestPar(currentTextOfActivityTextView);
adapter.notifyDataSetChanged();
I am deleting items from a Recylerview. I implemented a View.OnClickListener in my ViewHolder. Then used setOnClickListener() on the view, and I am deleting the rows from the onClick method.
When I click the view the first time the item deletes, on the second click I get an out of bounds exception as the getAdapterPosition() method returns -1.
My implementation:
public ItemViewHolder(View itemView) {
imageView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
Log.v("clicker", "position is " + getAdapterPosition()); //returns correct index on first deletion and -1 on the second
mItems.remove(getAdapterPosition);
notifyItemRemoved(getAdapterPosition);
}
Note:
Curiously, using the deprecated getPosition() will return the incorrect index position, but it will nevertheless delete every item in the Recylcerview until the last item is left, wherein when the last item is clicked, it will crash because of an out of bounds index issue.
Tell me if you need more code.
Thanks in advance.
Okay so I've been able to hightlight an item in a ListView, but it ends up highlighting every fourth item. I'm pretty sure that's because of recycling. Then I had the issue where the highlighted item would go back to normal after I scrolled, and that's also because of recycling. Is there any way to keep it highlighted, or to maybe stop the ListView from recycling?
This is what the code looks like right now...
runTimes.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> list, View v, int pos, long id) {
v.setSelected(true);
}
});
This is the code where the highlighted item goes back to normal after you scroll.
to hilight a row you should not touch the view at all. you should use listviews setItemChecked with a selector as the background of your view.
runTimes.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> list, View v, int pos, long id) {
runTimes.setItemChecked(pos,true);
}
});
you also need to make sure you keep track of the last position you selected so that you can deselect it when you select a new one
If you want to stop ListView from recycling you should think again if you really need a ListView.
To properly accomplish this with a ListView, though, you need to save the highlighted item states inside of your adapter. Then in getView highlight the items based on their position.
There have been A LOT of questions about saving the state of the ListView items, I'm sure you can find some.
I ended up finding another question that helped me figure out how to do it. This is what I did. In the OnClickListener I check to see if something has been pressed before. If it hasn't been pressed before, then I set the views background color, and prevRunView to the view. If something has been pressed before, then I change the previous view background color to white. After that I do the same thing as before, but for the different view.
if(runIndex == -1){
runIndex = pos;
v.setBackgroundColor(Color.parseColor("#A6A6A8"));
prevRunView = v;
}else{
prevRunView.setBackgroundColor(Color.parseColor("#FFFFFF"));
runIndex = pos;
v.setBackgroundColor(Color.parseColor("#A6A6A8"));
prevRunView = v;
}
In my adapter I wrote this code so it won't seem like it's recycling.
if(ScoreActivity.runIndex == position)
v.setBackgroundColor(Color.parseColor("#A6A6A8"));
else
v.setBackgroundColor(Color.parseColor("#FFFFFF"));
Currently the list when populated is starting with the view # the bottom of the list. Is there a way using listAdapters to force it to the top of the list?
Currently the orientation scrolls to the bottom on create. Is there a way to pin the screen to the top when it creates? http://imgur.com/wGTEy in this example you see that entry 1 on create is shoved upwards to make room for six... Instead I want it to populate like this. http://imgur.com/6Lg6e... entry 1 is the top of the list and 6 is pushed off to the bottom for the scroll.
If you look at the picture above you will notice it starts at the bottom of the list instead of at the top. Any Ideas?
mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mStrings);
setListAdapter(mAdapter);
registerForContextMenu(getListView());
populateFields();
private void populateFields() {
if (mRowId != null) {
Cursor note = mDbHelper.fetchDaily(mRowId);
startManagingCursor(note);
String body = note.getString(note.getColumnIndexOrThrow(NotesDbAdapter.KEY_DBODY));
mAdapter.clear();
if (!(body.trim().equals(""))){
String bodysplit[] = body.split(",");
for (int i = 0; i < bodysplit.length; i++) {
mAdapter.add(bodysplit[i].trim());
}
}
}
}
**edited to fix != string error.
You want the items later in the list to be at the top of the ListView? If so, check out this questions: Is it possible to make a ListView populate from the bottom?
You are completely changing the adapter, so the scroll position is lost in the process... You can use:
ListView listView = getListView();
int position = listView.getFirstVisiblePosition();
if (!(body.trim().equals(""))){
String bodysplit[] = body.split(",");
for (int i = 0; i < bodysplit.length; i++) {
mAdapter.add(bodysplit[i].trim());
}
}
listView.setSelection(position);
But this is not perfect as it is, if a row is added before position the index will be off. If your list contains unique values you can use ArrayAdapter#getPosition(), to find the new index.
While I still recommend using a CursorAdapter, because it handles large table data better, I want to address a point on efficiency with your ArrayAdapter code.
By using adapter.clear() and adapter.add() you are asking the ListView to redraw itself on every step... potentially dozens or hundreds of times. Instead you should work with the ArrayList directly and then ask the ListView to redraw once itself with ArrayAdapter#notifyDataSetChanged() after the loop completes.
How would one find the position of a specific item within a ListView? (Populated by SimpleCursorAdapter).
The reason I ask: The listview is set to singleChoice mode. When the user closes and reopens the app, I'd like the user's selection to be remembered.
The way I've done it so far is when the user clicks on an item, the ID of the chosen item is saved to preferences. What I need to learn is how to reselect the item in the activity's onCreate method once it's been repopulated.
My code for saving the selected item's ID:
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
Cursor c = (Cursor) l.getItemAtPosition(position);
selectedItem = c.getLong(c.getColumnIndex("_id"));
}
(I've tried googling, but only seem to find how to get the position of the selected item)
Thanks!
You should try
//SimpleCursorAdapter adapter;
final int position = adapter.getCursor().getPosition();
API Docs:
public abstract int getPosition ()
Since: API Level 1
Returns the current position of the
cursor in the row set. The value is
zero-based. When the row set is first
returned the cursor will be at positon
-1, which is before the first row. After the last row is returned another
call to next() will leave the cursor
past the last entry, at a position of
count().
Returns
the current cursor position.
Update
To get an item's position based on the id used by the adapter:
private int getItemPositionByAdapterId(final long id)
{
for (int i = 0; i < adapter.getCount(); i++)
{
if (adapter.getItemId(i) == id)
return i;
}
return -1;
}
To get an item's position based on the underlying object's properties (member values)
//here i use `id`, which i assume is a member of a `MyObject` class,
//and this class is used to represent the data of the items inside your list:
private int getItemPositionByObjectId(final long id)
{
for (int i = 0; i < adapter.getCount(); i++)
{
if (((MyObject)adapter.getItem(i)).getId() == id)
return i;
}
return -1;
}
I do this straightforward in my own app:
long lastItem = prefs.getLong(getPreferenceName(), -1);
if (lastItem >= 0) {
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
if (lastItem == cursor.getLong(0)) {
spinner.setSelection(cursor.getPosition());
break;
}
cursor.moveToNext();
}
}
Spinner is populated with the cursor's contents, so I just look through them and compare with the selected item id. In your case that would be a ListView.
When you say, "...reselecting the item in the activity's onCreate method...", do you mean that when the user returns to the ListView activity, whatever item was previously chosen, is now currently at the top of the screen (assuming enough items appear in the list below it)?
If so, then from onListItemClick, you should also make an effort to save the value of position, since it tells you the position in the list of the selected item. This would allow you to not need to reverse-lookup the position from the _id.
Or is that for some reason not an option for your purposes? Do you really need to instead figure out the position from the _id?