How to refresh listview in android without scrolling - android

I am dynamically adding items to my listview my code works fine but my problem is when the listview is updated it is going to the starting position (items are added but scroll view begins from initial position).I am using listview inside fragment.I want to avoid that scrolling to initial position.
CODE
ListAdapter adapter =
new SimpleAdapter(getContext(), productsList, R.layout.list_notify, new String[]{"id","title","des"},
new int[]{R.id.id, R.id.title,R.id.des});
lv.setAdapter(adapter);
lv.invalidateViews();
Reference : How to refresh Android listview?
ListView Refresh in Android
Refresh Listview in android
Android refresh listview in fragment
How to refresh Android listview?

ListView is officially legacy. Try to use RecyclerView then you will be able to tell that you don't update whole list with methods like notifyItemChanged(position)...
In your case you will call notifyItemRangeInserted(position, count)
https://developer.android.com/guide/topics/ui/layout/recyclerview

Try smoothScrollToPosition on your listview.
See this, pretty similar if I understand correct what you want.

So in order to get that scrolling to stop you basically have to block the listview from laying out its children so first off you have to create a custom listview something like
public class BlockingListView extends ListView {
private boolean mBlockLayoutChildren;
public BlockingListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setBlockLayoutChildren(boolean block) {
mBlockLayoutChildren = block;
}
#Override
protected void layoutChildren() {
if (!mBlockLayoutChildren) {
super.layoutChildren();
}
}
}
then you can use it like this for example
int firstVisPos = mListView.getFirstVisiblePosition();
View firstVisView = mListView.getChildAt(0);
int top = firstVisView != null ? firstVisView.getTop() : 0;
// Block children layout for now
mListView.setBlockLayoutChildren(true);
// Number of items added before the first visible item
int itemsAddedBeforeFirstVisible = ...;
// Change the cursor, or call notifyDataSetChanged() if not using a Cursor
mAdapter.swapCursor(...);
// Let ListView start laying out children again
mListView.setBlockLayoutChildren(false);
// Call setSelectionFromTop to change the ListView position
mListView.setSelectionFromTop(firstVisPos + itemsAddedBeforeFirstVisible, top);
the setBlockLayoutChildren being true is what will stop your listview from scrolling and of course you can set whatever else you would like it to do
you may also just want to look into recyclerview though may make your life easier

I used a gridview instead of my listview and it solved my problem
set 1 item per row can act as listview in my code
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/grid"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
**android:numColumns="1"**
android:verticalSpacing="10dp"
android:horizontalSpacing="10dp"
android:scrollbarStyle="outsideOverlay"
android:verticalScrollbarPosition="right"
android:scrollbars="vertical">
reference : Custom layout as an item for a grid view

Related

How to implement 2 different lists with 2 different ITEMS in a single listView with a sectional divider between the listView?

I want to implement a listView with 2 different lists which is using 2 different Items and want to display them using a same ListView.
Both lists are separated with a sectional divider
The above listView is having a custom selector imageButton in the item
below listView has dateDividers for their items
You should use
https://github.com/commonsguy/cwac-merge
in order to create and merge different adapters with the data you need. You should also insert a header view.
Here follows an example:
#BindView(R.id.lv_validation_errors)
ListView lvValidationErrors;
private List<Notification> notificationList = new ArrayList<>();
public MergeAdapter mergeAdapter = new MergeAdapter();
public ArrayAdapter errorAdapter;
private ArrayList<String> errorList = new ArrayList<>();
public ArrayAdapter alertAdapter;
private ArrayList<String> alertList = new ArrayList<>();
First I set the ListView (using ButterKnife, which does the findViewById stuff) then
errorAdapter = new ArrayAdapter(this, R.layout.adapter_error, R.id.text1, errorList);
mergeAdapter.addView(header("Erros"), false);
mergeAdapter.addAdapter(errorAdapter);
I created the adapters I want to mmerge and added the view for the section header (dont forget to set a value to your list to prevent it from being null) then
alertAdapter = new ArrayAdapter(this, R.layout.adapter_warning, R.id.text1, alertList);
mergeAdapter.addView(header("AdvertĂȘncias"), false);
mergeAdapter.addAdapter(alertAdapter);
same as the previous step and finally:
lvValidationErrors.setAdapter(mergeAdapter);
to set the merged adapter to the ListView.
for that you have to take one list with type Object
List<Object> listItems = new ArrayList<>();
your both different list values add in above listItems .
then after bind your adapter with listItems and when you bind your item in your BindView() write like below.
if(listItems instanceof firstListItemModel)
// bind item from your first list
else if(listItems instanceof secondListItemModel)
// bind item from your second list
As i can understand, you want a vertically half divided screen with containing ListView in each of them.
And you are saying that bottom ListView is completely based on upper ListView.
bottom ListView should load according to Upper ListView selection.
if Yes, then you can use weight to show bothListViews(upper and bottom) on layout like this :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/ll_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp">
<ListView
android:id="#+id/list_view1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<ListView
android:id="#+id/list_view2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
Then you can take set Adapter on list_view2 on list_view1 item click :
ListView upperListView=(ListView) findViewById(R.id.list_view1);
ListView bottomListView=(ListView) findViewById(R.id.list_view2);
upperListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
// You can put your logic here to fetch data according to upper ListView item clicked position. I am taking temp. data for now.
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1, arrayList//your desired list here);
((ListView) bottomListView.setAdapter(adapter);
}
});
That's it!!

ListView addView(View child, int index) not working

I'm trying to add a custom view to my listview (which is already populated) using this code:
listview.addView(customView,rowNumber);
but my app crashes..
I realized that if I use this code :
list.addFooterView(customView);
it works, but I can't choose where to place the view . What can I do?
The addFooterView works because it adds on the bottom. You can't add anything in the Listview just putting it using addView. Instead, do the following example:
After doing it:
ArrayList<Object> _views = new ArrayList<Object>();
_views.add(new View());
_views.add(new View2());
ArrayAdapter<Object> _adapter = new ArrayAdapter<Object>(getActivity(), android.R.layout.simple_list_item_1, _views)
mListView.setAdapter(_adapter);
You only need to add the new View into ArrayList and notify the adapter that the listview had changed such as:
_views.add(new View3());
_adapter.notifyDataSetChanged();

keep Scroll position with every refresh in list view

I set a timer in my app, I could get some information from a web service and resulted in a list view to display. Now my problem is that every time the timer runs, scroll back to the beginning ...
how can i keep Scroll position with every refresh in list view ?
part of my code:
runOnUiThread(new Runnable() {
public void run() {
/**
* Updating parsed JSON data into ListView
* */
ListAdapter adapter = new SimpleAdapter(DashboardActivity.this,
all_chat,
R.layout.list_item,
new String[] { TAG_FULLNAME,
TAG_DATE,
TAG_MESSAGE },
new int[] { R.id.fullname,
R.id.date,
R.id.message }
);
// updating listview
setListAdapter(adapter);
}
});
TNx.
Do not call setAdapter(). Do something like this:
ListAdapter adapter; // declare as class level variable
runOnUiThread(new Runnable() {
public void run() {
/**
* Updating parsed JSON data into ListView
*/
if (adapter == null) {
adapter = new SimpleAdapter(
DashboardActivity.this, all_chat, R.layout.list_item, new String[]{TAG_FULLNAME, TAG_DATE, TAG_MESSAGE},
new int[]{R.id.fullname, R.id.date, R.id.message});
setListAdapter(adapter);
} else {
//update only dataset
allChat = latestetParedJson;
((SimpleAdapter) adapter).notifyDataSetChanged();
}
// updating listview
}
});
You can add the following attribute to your ListView in xml.
android:stackFromBottom="true"
android:transcriptMode="alwaysScroll"
Add these attributes and your ListView will always be drawn at bottom like you want it to be in a chat.
or if you want to keep it at the same place it was before, replace alwaysScroll to normal
in the android:transcriptMode attribute.
Cheers!!!
I had the same issue, tried a lot of things to prevent the list from changing its scroll position, including:
android:stackFromBottom="true"
android:transcriptMode="alwaysScroll"
and not calling listView.setAdapter();
None of it worked until I found this answer:
Which looks like this:
// save index and top position
int index = mList.getFirstVisiblePosition();
View v = mList.getChildAt(0);
int top = (v == null) ? 0 : (v.getTop() - mList.getPaddingTop());
// ...
// restore index and position
mList.setSelectionFromTop(index, top);
Explanation:
ListView.getFirstVisiblePosition() returns the top visible list item. But this item may be partially scrolled out of view, and if you want to restore the exact scroll position of the list you need to get this offset. So ListView.getChildAt(0) returns the View for the top list item, and then View.getTop() - mList.getPaddingTop() returns its relative offset from the top of the ListView. Then, to restore the ListView's scroll position, we call ListView.setSelectionFromTop() with the index of the item we want and an offset to position its top edge from the top of the ListView.
There's a good article by Chris Banes. For the first part, just use ListView#setSelectionFromTop(int) to keep the ListView at the same visible position. To keep the ListView from flickering, the solution is to simply block the ListView from laying out it's children.

Show header always in Android ListView

I need to show ListView header always, even when ListView has no items.
It is possible to do? Or better add header in ListView as first item?
If there's no elements for list and you are not adding the adapter, just add this:
mListView.setAdapter(null);
and the header will appear.
In your activity in onCreate You can add header by adding code:
View headerView = ((LayoutInflater)this.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.header_view, null, false);
getListView().addHeaderView(headerView);
In most cases no need to add header, You can just add items in xml layout.
As case you can use http://viewpagerindicator.com/
and set getListView().setEmptyView(emptyView); // when your listView has no items
My solution was to manually toggle the empty view.
/**
* Custom empty view handling because we don't want the
* list to be hidden when the empty view is displayed,
* since the list must always display the header.
*/
private void toggleEmptyView(Adapter adapter)
{
final View emptyView = findViewById(R.id.empty_view);
adapter.registerDataSetObserver(new DataSetObserver() {
#Override
public void onChanged() {
emptyView.setVisibility(adapter.getCount() == 0 ? View.VISIBLE : View.GONE);
}
});
}
Irrespective of whether your ListView has elements or not, header will be visible always. If there are elements in ListView, header will scroll along with them else it will be on top always.
For how to add header view to ListView, you can refer other answers provided by users.

How to apply android layout animation only to children above a certain index?

I have a ListView containing a series of notes.
Currently I use a layout animation to slide all the notes in from the side when the list first loads; this works perfectly.
However, I'm trying to figure out how to apply a layout animation only to list items below a certain point. Say I delete an item on the list: I'd like all items below it to shift up into the deleted note's old spot.
I've tried finding a way to customize the animation delays or interpolators by child index but haven't found anything appropriate for this location. Is there a way to do this using a custom layout animation (such as extending LayoutAnimationController) or would I have to do this low level and animate each view individually?
Any suggestions?
Create your animation and call it in your list OnItemClickListener. After that you might use the adapter's notifyDataSetChanged to refresh the list content.
In this example, I created a method called removeListItem with receives the row you want to remove and the position of that row in you list content array.
public class MainActivity extends ListActivity implements OnItemClickListener{
ArrayList<String> values;
ArrayAdapter<String> adapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
values = generateMockData(50);
adapter = new ArrayAdapter<String>(
this, android.R.layout.simple_list_item_1, values);
setContentView(R.layout.activity_main);
getListView().setAdapter(adapter);
getListView().setOnItemClickListener(this);
}
private ArrayList<String> generateMockData(int number) {
ArrayList<String> result = new ArrayList<String>();
for(int i = 0; i < number; i++)
result.add(""+i+" "+ (int)Math.random() * 13);
return result;
}
private void removeListItem(View rowView, final int positon) {
Animation anim = AnimationUtils.loadAnimation(this,
android.R.anim.slide_out_right);
anim.setDuration(500);
rowView.startAnimation(anim);
new Handler().postDelayed(new Runnable() {
public void run() {
values.remove(positon);//remove the current content from the array
adapter.notifyDataSetChanged();//refresh you list
}
}, anim.getDuration());
}
public void onItemClick(AdapterView<?> arg0, View row, int position, long arg3) {
if(position == YOUR_INDEX) //apply your conditions here!
removeListItem(row,position);
}
I had a very similar problem and was able to find a solution with a simple View subclass that allows you to use a layout animation controller to animate only the views that you specify. Please see this link:
Can LayoutAnimationController animate only specified Views
In your layout xml, try adding:
<ListView
android:id="#android:id/list"
...
android:animateLayoutChanges="true"/>
This will automatically animate insertions and deletions from your listview.
If you have a custom animation, named anim_translate_left, use instead:
<ListView
android:id="#android:id/list"
...
android:animateLayoutChanges="#anim/anim_translate_left"/>
Source: Google API's

Categories

Resources