Android linearLayoutManager.setStackFromEnd() not working for chat show last element - android

I'm working on a chat application. Now, while fetching messages from the back-end for the first time, i want my Linear Layout pointer to move to the last message in the list, so that the user always sees the latest messages on opening the chat fragment.
This is my code:
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(homeScreenActivity);
linearLayoutManager.setStackFromEnd(true);
linearLayoutManager.setReverseLayout(true);
chatRecycler.setLayoutManager(linearLayoutManager);
Thanks in advance!

If you dont know the when data is entered, you can make addOnLayoutChangeListener on your recycler view.
And in addOnLayoutChangeListener
chatRecycler.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v,
int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (bottom < oldBottom) {
chatRecycler.postDelayed(new Runnable() {
#Override
public void run() {
recycler_discussion.scrollToPosition(
recycler_discussion.getAdapter().getItemCount() - 1);
}
}, 50);
}
}
});

Related

Creating a locked ScrollView in Android

I'm trying to implement a ScrollView in Android that doesn't scroll when adding an item above the current scroll position.
The default implementation of ScrollView behaves as following:
Adding an item above the current scroll position:
Adding an item below the current scroll position:
How can I "lock" the ScrollView prior to adding an item above the current scroll position?
This is my layout file, I've currently overridden both the ScrollView and LinearLayout, but haven't made any alterations yet.
<LinearLayout
android:layout_height="fill_parent"
android:orientation="vertical"
android:layout_width="fill_parent">
<LinearLayout
android:id="#+id/LinearLayout02"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_alignParentBottom="true">
<Button
android:id="#+id/Button02"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" android:text="Add To Top"
android:onClick="addToStart">
</Button>
<Button
android:id="#+id/Button03"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Add to End"
android:onClick="addToEnd">
</Button>
</LinearLayout>
<com.poc.scroller.locable.lockablescrollerpoc.LockedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fillViewport="true"
android:scrollbarAlwaysDrawVerticalTrack="true"
android:verticalScrollbarPosition="right"
android:fadeScrollbars="false"
android:background="#color/scrollColor">
<com.poc.scroller.locable.lockablescrollerpoc.LockedLinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="#+id/Container">
</com.poc.scroller.locable.lockablescrollerpoc.LockedLinearLayout>
</com.poc.scroller.locable.lockablescrollerpoc.LockedScrollView>
</LinearLayout>
</android.support.constraint.ConstraintLayout>
Example Source Code:
https://github.com/Amaros90/android-lockable-scroller-poc
Thank you!
You can easily get the opposite behavior by using addOnLayoutChangeListener of your LinearLayout and reset the ScrollView's ScrollY. Here is the implementation in your ScrollViewActivity.onCreate
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.scrollview);
_linearLayout = (LockedLinearLayout)findViewById(R.id.Container);
_scrollView = (LockedScrollView)findViewById(R.id.ScrollView);
_layoutInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
_linearLayout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
_scrollView.scrollTo(0, _scrollView.getScrollY() + (bottom - oldBottom ));
}
});
addExampleImage(10, _linearLayout);
}
Then you can easily flag the addToEnd function or check where the child was added to avoid changing scroll when some child is added to the bottom.
You can try to use RecyclerView and implement onDataSetChanged(). Then detect whether add to TOP or add to END button was pressed. Then use scrollToPositionWithOffset(int, int) to manage the scroll.
For example:
//Scroll item 3 to 20 pixels from the top
linearLayoutManager.scrollToPositionWithOffset(3, 20);
For restoring the scroll position of a RecyclerView, this is how to save the scroll positions (the two arguments for the method scrollToPositionWithOffset(int, int)):
int index = linearLayoutManager.findFirstVisibleItemPosition();
View v = linearLayoutManager.getChildAt(0);
int top = (v == null) ? 0 : (v.getTop() - linearLayoutManager.getPaddingTop())
Implementing this worked for me. You can check out the example app I added in the original question.
public class LockableScrollView extends ScrollView {
private boolean _enabled = true;
public LockableScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
super.setFillViewport(true);
}
#Override
public void addView(View layout, ViewGroup.LayoutParams params) {
super.addView(layout, params);
((ViewGroup)layout).setOnHierarchyChangeListener(new OnHierarchyChangeListener() {
#Override
public void onChildViewAdded(View layout, View item) {
if (_enabled) {
item.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View item, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
item.removeOnLayoutChangeListener(this);
int scrollViewY = ((LockableScrollView)item.getParent().getParent()).getScrollY();
int layoutPosition = ((View)item.getParent()).getTop();
boolean shouldScroll = item.getTop() + layoutPosition <= scrollViewY || item.getBottom() + getTop() <= scrollViewY;
if (shouldScroll) {
final int childViewHeight = item.getHeight();
((View)item.getParent()).addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View layout, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
layout.removeOnLayoutChangeListener(this);
LockableScrollView scrollView = ((LockableScrollView)layout.getParent());
scrollView.scrollTo(scrollView.getScrollX(), scrollView.getScrollY() + childViewHeight);
}
});
}
}
});
}
}
#Override
public void onChildViewRemoved(View layout, View item) {
if (_enabled) {
int scrollViewY = ((LockableScrollView)layout.getParent()).getScrollY();
int layoutPosition = layout.getTop();
boolean shouldScroll = item.getTop() + layoutPosition <= scrollViewY || item.getBottom() + getTop() <= scrollViewY;
if (shouldScroll) {
final int childViewHeight = item.getHeight();
layout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View layout, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
layout.removeOnLayoutChangeListener(this);
LockableScrollView scrollView = ((LockableScrollView)layout.getParent());
scrollView.scrollTo(scrollView.getScrollX(), scrollView.getScrollY() - childViewHeight);
}
});
}
}
}
});
}
}
you can do it easily just get it..
first of all in onclick event store the int value by
using getFirstVisiblePosition()
example.
in onclick of add element button do this---
int currentpos = recycleview.getFirstVisiblePosition();
now after you insert elment to list/recyclerview---
recyclerview.scrolltoposition(currentpos);
thats it try if you want to do it without error..
Good Luck :)

Send current view to ViewModel using DataBinding

I have a BindingAdapter like
#BindingAdapter({ "onGlobalLayout" })
public static void setOnGlobalLayout(View view, ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener) {
ViewTreeObserver vto = view.getViewTreeObserver();
vto.addOnGlobalLayoutListener(onGlobalLayoutListener);
}
In XML I have a View like
<View
android:id="#+id/viewId"
bind:onGlobalLayout="#{viewModel.onLayoutListener}"
/>
In ViewModel I have
public ViewTreeObserver.OnGlobalLayoutListener onLayoutListener(View view){
return new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// get x,y,width, height of view
}
};
}
Now I want to send the current View to ViewModel so I add a parametter View view to onLayoutListener but in XML I don't know how to send it.
Any help or suggestion would be great appreciated
UPDATE
Thank #pskink for suggest a better way for checking layout change.
With android:onLayoutChange I don't need BindingAdapter
<View
android:id="#+id/viewId"
android:onLayoutChange="#{viewModel.onLayoutListener(viewId)}"
/>
Java
public View.OnLayoutChangeListener onLayoutUpdateWeightSuccessfulListener(final View v) {
return new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
// left, top
}
};
}
simply pass your View id, it should work
bind:onGlobalLayout="#{viewModel.onLayoutListener(viewId)}"

Recyclerview not scrolling to end when keyboard opens

I am using recylerview in my application and whenever new element is added to recyclerview, it scrolls to last element by using
recyclerView.scrollToPosition(adapter.getCount());
But, whenever keyboard opens(because of editTextView), it resizes the view and recyclerview gets smaller, but couldn't scroll to last element.
android:windowSoftInputMode="adjustResize"
I even tried to used the following code to scroll to last element, but it didn't work
editTextView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if(hasFocus){
recyclerView.scrollToPosition(chatAdapter.getItemCount());
}
}
});
I can try adjustPan to shift the pan up, but it is hiding my toolbar.
Please suggest any way to rectify the issue.
You can catch keyboard up changes using recyclerview.addOnLayoutChangeListener().
If bottom is smaller than oldBottom then keyboard is in up state.
if ( bottom < oldBottom) {
recyclerview.postDelayed(new Runnable() {
#Override
public void run() {
recyclerview.smoothScrollToPosition(bottomPosition);
}
}, 100);
}
Add this your activity or fragment:
if (Build.VERSION.SDK_INT >= 11) {
recyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v,
int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (bottom < oldBottom) {
recyclerView.postDelayed(new Runnable() {
#Override
public void run() {
recyclerView.smoothScrollToPosition(
recyclerView.getAdapter().getItemCount() - 1);
}
}, 100);
}
}
});
}
It works for me
LinearLayoutManager layoutManager = new LinearLayoutManager(context);
layoutManager.setStackFromEnd(true);
recyclerView.setLayoutManager(layoutManager);
I found the postDelayed to be unnecessary and using adapter positions doesn't account for when the recycler is scrolled to somewhere in the middle of an item. I achieved the look I wanted with this:
recycler.addOnLayoutChangeListener((view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
if (bottom < oldBottom) {
messageRecycler.scrollBy(0, oldBottom - bottom);
}
})
Although this an old question, I experienced this problem today and I found out that none of of the above method works. This is my solution
recyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v,
int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (bottom < oldBottom) {
final int lastAdapterItem = mAdapter.getItemCount() - 1;
recyclerView.post(new Runnable() {
#Override
public void run() {
int recyclerViewPositionOffset = -1000000;
View bottomView = linearLayoutManager.findViewByPosition(lastAdapterItem);
if (bottomView != null) {
recyclerViewPositionOffset = 0 - bottomView.getHeight();
}
linearLayoutManager.scrollToPositionWithOffset(lastAdapterItem, recyclerViewPositionOffset);
}
});
}
});
}
This works.
private RecyclerView mRecyclerView;
private RecyclerView.Adapter mAdapter;
private LinearLayoutManager mManager;
...
mManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mManager);
(initialize and set adapter.)
mRecyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (bottom < oldBottom) scrollToBottom();
}
});
private void scrollToBottom() {
mManager.smoothScrollToPosition(mRecyclerView, null, mAdapter.getItemCount());
}
recyclerView.smoothScrollToPosition(recyclerView.getAdapter().getItemCount());
It works properly in support library version 27.0.1
There is nothing to set in the manifest.
val currentScrollPosition = 0
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
currentScrollPosition = recyclerView.computeVerticalScrollOffset() + recyclerView.computeVerticalScrollExtent()
}
override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) { }
})
storyList.addOnLayoutChangeListener { view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
if (bottom < oldBottom) {
if (currentScrollPosition >= recyclerView.computeVerticalScrollRange()) {
recyclerVIew.post {
recyclerView.overScrollMode = RecyclerView.OVER_SCROLL_NEVER
recyclerView.smoothScrollBy(0, recyclerView.computeVerticalScrollRange() - recyclerView.computeVerticalScrollOffset() + recyclerView.computeVerticalScrollExtent())
}
}
} else {
recyclerView.overScrollMode = RecyclerView.OVER_SCROLL_ALWAYS
}
}
layoutManager.scrollToPositionWithOffset(chatMessageAdapter.getItemCount() - 1, 0);
You can use addOnItemTouchListener to see the scrolling change:
private Boolean scrollListerActivate = true;
mRecyclerViewTop.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if(scrollListerActivate == true) {
scrollListTopManager();
}
}
});
To scroll for last (or other) position you should use scrollListTopManager method:
public void scrollListTopManager() {
int position = 0;
int itemCount = mRecyclerViewTop.getAdapter().getItemCount();
position = itemCount - 1;
mRecyclerViewTop.scrollToPosition(position);
}
You should also use addOnItemTouchListener to stop using scrollListTopManager method, if you want to scroll manually:
mRecyclerViewTop.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
#Override
public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent
motionEvent) {
scrollListerActivate = false;
return false;
}
#Override
public void onTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent)
{}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean b) {}
});
When you open chat you will get last message at the end of recycle view:
layoutManager.setStackFromEnd(true);
recyclerView.setLayoutManager(layoutManager);
Below code will make adjustments according to keyboard!
recyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
if ( bottom <= oldBottom) {
recyclerview.postDelayed(new Runnable() {
#Override
public void run() {
recyclerview.smoothScrollToPosition(bottom);
}
}, 100);
}
}
});
Bind the RecyclerView inside NestedScrollView
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</android.support.v4.widget.NestedScrollView>

Android: how to make transition animations on toolbar's menu icons?

If you use the last version of WhatsApp you will notice that if you long click a textbox in a chat, then the menu icons on the toolbar will change with a nice rotating animation.
How could I reproduce that effect? I know I should invalidate the menu but not how to make the animation.
Use a Toolbar.
Wait for the Toolbar to have its items inflated.
Find the item in question
Animate the item
Example:
mToolbar = (Toolbar) findViewById(R.id.toolbar);
mToolbar.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
View item = mToolbar.findViewById(R.id.action_add_item);
if (item != null) {
mToolbar.removeOnLayoutChangeListener(this);
item.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ObjectAnimator animator = ObjectAnimator
.ofFloat(v, "rotation", v.getRotation() + 180);
animator.start();
}
});
}
}
});
Note R.id.action_add_item is the id attribute of the MenuItem.

android- How to get the width and height of the list view dynamically

In my application i want display list view using adapter. But i want get the current height and width of the list view (means after generating the list using adapter). how to get it. can anybody help me.
thanks
Use getWidth() and getHeight() method of ListView in onWindowFocusChanged to get the width and height.
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
System.out.println("Width:" + listview.getWidth());
System.out.println("Height:" + listview.getHeight());
}
To get current width or height of any view, you need to set onLayoutChange listener
public class MainActivity extends Activity implements OnLayoutChangeListener {
private View newView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
newView = getLayoutInflater().inflate(R.layout.main_activity, null);
newView.addOnLayoutChangeListener(this);
setContentView(newView);
}
public void onLayoutChange(View v, int left, int top, int right,
int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
//Here you can get size of you ListView, e.g:
mylistView.getWidth();
}
#Override
protected void onDestroy() {
super.onDestroy();
newView.removeOnLayoutChangeListener(this);
}
}
27Nov15 - This is a good answer, I think you should release the resource too though. This might not matter so much for an Activity, but the same approach can be used by fragments where they may come and go more frequently.

Categories

Resources