Android ListAdapter start # top - java

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.

Related

Anomaly in my RecyclerView item adding code

I got 3 types of items I want to add to my RecyclerView, a button (item with a button inside of it), a dummy (item with empty display), a normal item (item with some stuff inside of it).
I'm going to write a scenario that describes the adding/remove process when it should work but it isn't for some reason.
Scenario:
-add a button + dummy to RecyclerView, they get displayed just fine.
WidgetItem dummy = new WidgetItem(true);
arrayList.add(0, dummy);
ca.notifyItemInserted(0);//ca is my adapter
WidgetItem btnHolder = new WidgetItem();
btnHolder.setButton(true);
arrayList.add(0, btnHolder);
ca.notifyItemInserted(0);
-add another normal item + 2 dummies -->fine
if (nbrItems % 3 == 0) {
for (int i = 0; i < 2; i++) {
WidgetItem dummy = new WidgetItem(true);
dummy.setTilte("dummy" + i);
arrayList.add(0, dummy);
ca.notifyItemInserted(0);
}
}
arrayList.add(0, wItem);
ca.notifyItemInserted(0);
-remove 1 dummy + add a normal item -->fine
if ((nbrDummy == 3)||(nbrDummy == 2))
for (int i = 0; !foundDummy; i++) {
if (cii.get(i).getDummy()) {
arrayList.remove(i);
ca.notifyItemRemoved(i);
foundDummy = true;
}
}
arrayList.add(0, wItem);
ca.notifyItemInserted(0);
-remove 1 dummy + add a normal item (the same as above) -->not fine
This's when the problem happens and instead of getting a 3rd 'normal item' i get another dummy instead in the display.
What confused me was that the "onCreateViewHolder" method in my custom adapter didn't even get called at this step while it got called just fine in the previous ones.
Even if the item that just got displayed was a dummy the "onCreateViewHolder" method should execute at least like how it should.
But the code just skipped right to the "onBindViewHolder" method.
I'm suspecting that maybe something happened after the deletion of that dummy but then again why it worked fine in the code above.
I tried "notifyItemRangeChanged" after deleting the dummy but the whole recyclerView got messed up.
So yeah, what's going on here?
My problem was due to how RecyclerView works I suppose.
After I delete an item from the list and create another, the same old back item that I deleted come back instead.
So after trying a couple of things i found out that putting recyclerView.getRecycledViewPool().clear(); between the items deletion and the creation code solved my problem.

How to Highlight Single Row or Item of Recycler View and Scroll the Highlighted Row to Top of screen

I want to Highlight a single row of recyclerview with sound one after another and scroll the highlight row to top of screen This is what i have done:
Here its code:
fun AnimateToSurahAlFeel(recyclerView: RecyclerView, layoutManager: LinearLayoutManager, currentPosition: Int) {
var position: Int = currentPosition / 1000
when (position) {
0 -> {
recyclerView.smoothScrollToPosition(0)
recyclerView.getChildAt(0).isSelected = true
}
4 -> {
recyclerView.smoothScrollToPosition(1)
recyclerView.getChildAt(0).isSelected = false
recyclerView.getChildAt(1).isSelected = true
}
11 -> {
recyclerView.smoothScrollToPosition(2)
recyclerView.getChildAt(1).isSelected = false
recyclerView.getChildAt(2).isSelected = true
}
17 -> {
recyclerView.smoothScrollToPosition(3)
recyclerView.getChildAt(2).isSelected = false
recyclerView.getChildAt(3).isSelected = true
}
21 -> {
recyclerView.smoothScrollToPosition(4)
recyclerView.getChildAt(3).isSelected = false
recyclerView.getChildAt(4).isSelected = true
}
28 -> {
recyclerView.smoothScrollToPosition(5)
recyclerView.getChildAt(4).isSelected = false
if (recyclerView.getChildAt(5) != null)
recyclerView.getChildAt(5).isSelected = true
}
}
}
In the Function currentPosition is Media player current position
Problem in this code is:
In the Screen shot Row 4 and 5 are currently not visible,when highlighting Row 4 and 5 the App crash and giving Null Pointer Exception, according to my knowledge these two row are not yet created that's why
recyclerview.getChildAt(4) or recyclerview.getChildAt(5) return null and that cause the App crash.
Now
How to fix the App crash that recyclerview.getchildAt(4) or recyclerview.getchildAt(5) return null and also getChildAt(position) return n-1 row, so the App crash at recyclerview.getchildAt(5) will occur anyhow but i want n Row because i want to highlight all rows
How to scroll the highlighted row to position 0 (at top)
i.e. Row 0 go up from screen and Row 1 take it position and so on...
I want to achieve like this the highlighted one is at top and that will go off from screen when another row is highlighted
You need time for View to Bind. Just for ex. you can post action.
....
17 -> {
recyclerView.smoothScrollToPosition(3)
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
recyclerView.getChildAt(2).isSelected = false
recyclerView.getChildAt(3).isSelected = true
}, 500);
}
....
But I strongly recommend you use some state Collection, which will save and handle states of your running and showing Views.
This is because recycler views don't have all the views inflated, but only the visible ones. This is by design and should not be tinkered with. Personally, I think you should use the recycling functionality.
You need to make the selected state part of your model in the adapter - the items in the adapter. Let's say this is called RowItem, so in your adapter you'd have a list of RowItems for example. Aside from the text in both languages, you need to add the selected state too. Then it's just a matter of getting the list's adapter, setting the correct position to selected and deselecting the ones you want.
For example, in your adapter:
fun select(position: Int) {
data[position].selected = true
notifyItemChanged(position)
// deselect all other positions that you don't want selected
}
When you bind the view holder you could do then:
public void onBindViewHolder(ViewHolder viewHolder, int position) {
val item = data[position]
viewHolder.itemView.selected = item.selected
// take care of the rest of the views
}
data would be a list where you store your RowItems
Now you can scroll with no problem and set the item to selected. Once the view is visible in the recycler view, the adapter will set the correct state.
It's fair to say I'm guessing a bit since there's no adapter code in your question, but the idea I think it's easy to understand. You need to change the state in the adapter and let the implementation of the recycler view handle it for you. After all the purpose is to get the recycler view to recycle your views based on the models adapted by the adapter.
Remember you can always get your adapter from the recycler view itself. In the end you can do something like this:
...
0 -> {
recyclerView.smoothScrollToPosition(0)
(recyclerView.adapter as MyAdapter).select(0)
}
Here MyAdapter would be the class name of your adapter
For the scrolling part you can take a look at this

How do you iterate through dynamically created objects?

Lets say you have an object like this and you want to have the program go through each dynamically created Checkbox to see if it has not been checked.
If has not been checked, then the program should create a notification alerting the user that one or more of these objects has not been checked.
What is the best way to have the program identify whether the checkbox is checked or not?
Each time I run the program, it only applies to the last created Checkbox regardless of how many checked or unchecked checkboxes proceed it.
Thank you for your time.
View ObjectView;
CheckBox check;
//A whole bunch of code here.
public void onClick(View arg0) {
if (check==null){
}
else if (check==null || check.isChecked()){
}
else {
ObjectView.getId();
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(getActivity())
.setSmallIcon(android.R.drawable.stat_notify_more)
.setContentTitle("Items missing")
.setContentText("One or more items are missing");
int ID_Notify = 01;
getActivity();
NotificationManager managenote = (NotificationManager)getActivity().getSystemService(Context.NOTIFICATION_SERVICE);
managenote.notify(ID_Notify, mBuilder.build());
Im gonna help you with an example of my code and try to explain it, aware that im not gonna babysit you (means you cant just copy paste) because i still have some work to do.
First, a new dynamic spinner will be created everytime you click a button (inside onClick) :
Spinner spinner = new Spinner(this);
spinner.setAdapter(spinChildAdapter);
parentSpinner.addView(spinner);
spinner.setId(totalDynamicChild); //the spinner's id will be the increment from 0
spinnderIdList.add(totalDynamicChild); //list of the dynamic spinner ID
totalDynamicChild++;
Then, we can access those dynamic Spinners with :
for(int i = 0; i < totalDynamicChild; i++)
{
Spinner s = (Spinner)findViewById(spinnderIdList.get(i));
//do something with the spinner's object here
}
Feel free to comment if you have some questions.

Add view to linear layout in background thread one at a time, not once all are done

I have a linear layout that I am adding several views to. Think linear layout of result items. It is possible for there to be up to 150 result items. Generating each view takes a bit, so I want them to show up as they become available.
Here is my current code:
for (final Dealership loc: locations) {
final int x = resultNumber;
view.post(new Runnable(){
public void run(){
if(loc != null && parent != null && currentLocation != null) {
View v = getResultView(x, loc, parent, currentLocation);
v.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
if(parentLayout != null) {
parentLayout.addView(v);
}
}
}
});
resultNumber++;
}
This works in that it runs in the background and adds all the items. The only problem is that all the views appear at once, after a few seconds. I would really like it if they appeared as soon as they were created.
Is there a way I can modify this code so that it works as desired? Am I going about this the wrong way?
Thanks!
It seems like instead of a LinearLayout, you'll want to instead go with a ListView.
A ListView would give you a scrolling list of elements that are backed by a Collection of some sort (Array, List, etc.), and you could update that list on the fly. This is helpful for when you are slowly aggregating data from an external resource.
ListView also does some optimizing under-the-hood, and will only render row views when they are shown on the screen. If it is taking some time to render all 150 views in your LinearLayout, a ListView would help you here as well.

ListView items aren't updating with notifyDataSetChanged()

Apparently the notifyDataSetChanged() only updates the visible items in the listview, I have a system that changes the background color of an item when its clicked (to dkgray), and I set everything else to transparent(the default), however other items that aren't visible remain selected(dkgray) (I only want the currently selected item to be dkgray). Is there a way to force notifyDataSetChanged() to update all items.
Here's example:
//makes all item backgrounds transparent
public void resetListViewBackground(){
for (int i = 0; i < listView.getChildCount(); i++){ //parent.getChildCount()
listView.getChildAt(i).setBackgroundColor(Color.TRANSPARENT);
}
}
//reloads the listview
private void reloadListView() {
listItems.clear();
adapter.notifyDataSetChanged();
listView.invalidateViews();
ArrayList<HashMap<String, String>> notesArrayList = dbTools.getAllNotes();
for (int i = 0; i < notesArrayList.size(); i++){
String temp = "";
if (notesArrayList.get(i).get("note").length() > 51){
temp = notesArrayList.get(i).get("note").substring(0,50).toString() + "...";
} else {
temp = notesArrayList.get(i).get("note").toString();
}
listItems.add(temp);
adapter.notifyDataSetChanged();
}
}
Everywhere I call resetListViewBackground(), I call reloadListView() after.
And this is what I use to highlight the selected item.
view.setBackgroundColor(Color.DKGRAY);
Also, the most common occurrence of this problem is that every 6th item is highlighted. The listview only shows about 4 items at a time.
The getChildCount() method won't work for all list items as view recycling is done.
Astral is right as he writes in the comments.
1) Create a custom adapter.(See http://windrealm.org/tutorials/android/listview-with-checkboxes-without-listactivity.php)
2)Inside the onListItemClick listener's onItemClick() method, call your adapter's notifyDataSetChanged() method(whenever the user clicks a list item).
I modified the project that I mentioned and posted it on my Dropbox.(Just import it in Eclipse and run).
Check it out at https://www.dropbox.com/s/gchccjzpkxk8n2z/Planets_modified.zip

Categories

Resources