I have a RecyclerView which im using to display messages, in a text app type way. By that I mean, new messages added to the RecyclerView should appear on the bottom.
Currently, im using .setStackFromEnd(true) on my LinearLayoutManager to make messages appear starting from the bottom:
layMng = new LinearLayoutManager(this);
layMng.setStackFromEnd(true);
mRecyclerView.setLayoutManager(layMng);
mRecyclerView.setAdapter(mAdapter);
What happens is, when enough messages are received and the list of messages is about to overflow past the top of the screen, it stops always showing the new messages at the bottom. I have to scroll down manually to see the new additions.
How can I get it to either always have the newest message be viewable or have the RecyclerView scroll down when a new item is received?
I tried various methods on my layout manager like .scrollToPosition() and .scrollToPositionWithOffset() but none of the suggestions i've seen so far have worked. A few of the top StackOverflow questions on the issue have top answers that dont even solve the original question..
So how is this done?
Please check layout_height. It should be android:layout_height="match_parent"
Run smoothScrollToPosition in UI thread
code snippet from my code
<android.support.v7.widget.RecyclerView
android:id="#+id/topic_screen_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="18dp"
/>
//here topicScreenList is referencing my Recylerview:
//-messageAdapter: referencing adpapter object
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
layoutManager.setStackFromEnd(true);
topicScreenList.setLayoutManager(layoutManager);
topicScreenList.setAdapter(messageAdapter);
}
after getting any new messages, following code gets called from backend service
-Here MessageItem is referencing new Message item consumed by adapter
#Override
public void onNewMessage(final MessageItem messageItem) {
if (getActivity() != null) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
messageAdapter.addMessageToEnd(messageItem);
topicScreenList.smoothScrollToPosition(messageAdapter.getItemCount());
}
});
}
}
//code from my Adapter
public void addMessageToEnd(MessageItem messageItem) {
messageItems.add(messageItem);
notifyItemInserted(messageItems.size() -1);
}
Try this, Am also developing a chat application, and it works perfectly for me
mRecyclerView.setAdapter(adaptorChat);
mRecyclerView.setHasFixedSize(true);
LinearLayoutManager llm = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
llm.setStackFromEnd(true);
mRecyclerView.setLayoutManager(llm);
XML
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/messages_list_sponsor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="#+id/edit_compose_msg_container" />
<LinearLayout
android:id="#+id/edit_compose_msg_container"
android:layout_width="match_parent"
android:layout_height="70dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:orientation="horizontal"
android:padding="3dp">
<EditText
android:id="#+id/compose_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:background="#drawable/white_edit_text_style"
android:hint="#string/enter_message"
android:inputType="textMultiLine"
android:minHeight="50dp"
android:paddingEnd="4dp"
android:paddingLeft="6dp"
android:paddingRight="4dp"
android:paddingStart="6dp"
android:singleLine="false"
android:textColor="#color/black"
android:textColorHint="#android:color/darker_gray" />
<ImageView
android:id="#+id/send_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="5"
android:clickable="true"
android:padding="15dp"
android:rotation="-35"
android:layout_marginBottom="5dp"
android:src="#drawable/message_send" />
</LinearLayout>
</RelativeLayout>
mRecyclerView.scrollToPosition(position); should work. It could be that it's trying to scroll before the position/adapter is updated. Try:
int delay = 500;
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
//scroll to bottom
mRecyclerView.smoothScrollToPosition(mAdapter.getItemCount());
}
}, delay);
if you have set mRecyclerView.setNestedScrollingEnabled(false) then remove this.
Related
Note: These are randomly generated addresses
Hey Guys, Learning Xamarin and I am trying to scroll my Frame Layour down and reveal a search bar for my list view. Here is what is happening:
I color coded my layouts to see if the sizes where a problem, but I dont think they are since my orange layout is plenty big to hold my two entries. Am I using the wrong layouts for this kind of application? I would appreciate any help!
Here is my translation code:
frameLayout.Animate().TranslationYBy(editSearch.Height).SetDuration(500).Start();
And my layout file:
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/swipeLayout">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxHeight="100dp"
android:id="#+id/frameLayoutParent"
android:background="#android:color/holo_green_dark">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/frameLayout"
android:background="#android:color/holo_green_light">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="300dp"
android:background="#android:color/holo_orange_light">
<Button
android:text="Add New Address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/addNewAddress"
android:maxHeight="20dp" />
</LinearLayout>
<ListView
android:paddingTop="50dp"
android:minWidth="25px"
android:minHeight="300dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/myListView"
android:maxHeight="300dp"/>
</FrameLayout>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/editSearch"
android:hint="Search ZipCodes"
android:textColor="#000"/>
</FrameLayout>
</android.support.v4.widget.SwipeRefreshLayout>
EDIT:
Here is my OnOptionsItemSelected Code, where my animation is triggered.
public override bool OnOptionsItemSelected(IMenuItem item)
{
switch (item.ItemId)
{
case Resource.Id.action_search:
//search icon has been clicked
if (isAnimating)
return true;
else
{
if (animateBool)
{
//list view is up
animation anim = new animation(myListView, myListView.Height - editSearch.Height);
anim.Duration = 500;
myListView.StartAnimation(anim);
anim.AnimationStart += Anim_AnimationStartDown; //listener for when animation has started
anim.AnimationEnd += Anim_AnimationEndDown;
classSwipeRefresh.Animate().TranslationYBy(editSearch.Height).SetDuration(500).Start();
}
else
{
animation anim = new animation(myListView, myListView.Height + editSearch.Height);
anim.Duration = 500;
myListView.StartAnimation(anim);
anim.AnimationStart += Anim_AnimationStartUp; //listener for when animation has started
anim.AnimationEnd += Anim_AnimationEndUp;
classSwipeRefresh.Animate().TranslationYBy(-editSearch.Height).SetDuration(500).Start();
}
animateBool = !animateBool;
return true;
}
default:
return base.OnOptionsItemSelected(item);
}
}
Learning Xamarin and I am trying to scroll my Frame Layour down and reveal a search bar for my list view. Here is what is happening:
I use your code to test, but I have no problem when translating by Y in SwipeRefreshLayout__Refresh event.
public class MainActivity : AppCompatActivity
{
string[] items;
ListView listview1;
SwipeRefreshLayout swiplayout;
FrameLayout framelayout;
EditText edittext;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.activity_main);
listview1 = FindViewById<ListView>(Resource.Id.myListView);
swiplayout = FindViewById<SwipeRefreshLayout>(Resource.Id.swipeLayout);
framelayout = FindViewById<FrameLayout>(Resource.Id.frameLayout);
edittext = FindViewById<EditText>(Resource.Id.editSearch);
swiplayout.Refresh += Swiplayout_Refresh;
items = new string[] { "Vegetables", "Fruits", "Flower Buds", "Legumes", "Bulbs", "Tubers" };
listview1.Adapter = new ArrayAdapter(this, Android.Resource.Layout.SimpleListItem1, items);
}
private void Swiplayout_Refresh(object sender, EventArgs e)
{
framelayout.Animate().TranslationYBy(edittext.Height).SetDuration(500).Start();
}
}
This is my screenshot:
If you want to filter ListView, I suggest you can use SearchView in Toolbar to filter listview data, please take a look this sample:
https://github.com/Cheesebaron/SearchView-Sample/tree/master/SearchViewSample
I want to toggle visibility of some elements. I have read some Q/A that the main problem was doing this in main thread but I am running the code in runOnUiThread but not works yet:
TextView progress;
TextView registrationFailed;
Button tryAgainRegistration;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.registration);
sp = this.getSharedPreferences("settings", this.MODE_PRIVATE);
progress = (TextView) findViewById(R.id.progress);
registrationFailed = (TextView) findViewById(R.id.registrationFailed);
tryAgainRegistration=(Button) findViewById(R.id.tryAgainRegistration);
Setstat(0); // For test
}
public void Setstat(final Integer a){
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.e("progress",a.toString()); // It shows 0 and 1 correctly
if (a == 1) {
progress.setVisibility(View.VISIBLE);
registrationFailed.setVisibility(View.GONE);
tryAgainRegistration.setVisibility(View.GONE);
} else {
progress.setVisibility(View.GONE);
registrationFailed.setVisibility(View.VISIBLE);
tryAgainRegistration.setVisibility(View.VISIBLE);
}
}
});
}
for more details this is the UI xml:
<TextView
android:id="#+id/progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal|center_vertical"
android:text="Registeration in progress"
android:visibility="visible" />
<TextView
android:id="#+id/registrationFailed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal|center_vertical"
android:text="Registeration in progress"
android:visibility="gone" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Try again"
android:id="#+id/tryAgainRegistration"
android:visibility="gone"
/>
Try running this in on create.
Just to see if it is because of the threads or not:
findViewById(R.id.yourView).setVisibility(View.GONE);
The runOnUiThread method needs a reference to the activity.
Otherwise it doesn't know which one the UI thread is.
You might get a warning about memory leaks. If you are worried about that, google for WeakReference.
((Activity)context).runOnUiThread(new Runnable() {
My specific problem was that I had created the XML view using constrainLayout but modified it manually without removing original settings of constrainLayout. So I guess the layout was not well recognized by system Ui.
In fact I made a mistake by manually renaming android.support.constraint.ConstraintLayout to LinearLayout as the root element.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context=".Registration"
tools:showIn="#layout/activity_registration">
I am using a recyclerview that the viewholder entry has an imageview. Both have an onclick listener. If the child image view responded to the onclick, I don't want the recylerview onClick to respond to it. If the clicked location is not on the imageviewer, I want the recyclerview to respond. My research so far shows that usually this situation can be handled by using the same function to respond to both onClick events, but the problem is that recyclerview onclick and its child imageview onClick are different. I don't see how they can be handled in the same function. Right now I have a hacking workaround to use a global variable to indicate that the imageview has already responded to this click, and put up a 200ms delay on recyclerview listener to check the global variable before responding. Is there a more proper way of doing this?
I just noticed that if I add an "onClick" for every type of child views of the recycler view and remove the listener on the recyclerview, that can work. Right now I only have two child views anyway. Although I will be adding a lot more, it's still manageable. If there's no better way, I'll probably just do that.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="true"
android:paddingLeft="16dp"
android:paddingRight="4dp"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:clickable="true"
android:background="?android:attr/selectableItemBackground"
android:orientation="vertical">
<TextView
android:id="#+id/lstItemName"
android:textColor="#color/title"
android:textSize="16dp"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="#+id/lstItemReportby"
android:layout_gravity="left"
android:layout_width="0dp"
android:layout_height="wrap_content"
/>
<ImageView
android:id="#+id/lstItemMap"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginRight="2dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"/>
</android.support.constraint.ConstraintLayout>
public class TrackedItemListAdapter extends RecyclerView.Adapter<TrackedItemListAdapter.MyViewHolder>{
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView m_itemName, m_itemDes, m_itemReportedBy, m_itemHiddenText;
public ImageView m_itemMapDrop;
public MyViewHolder(View view) {
super(view);
m_itemName = (TextView) view.findViewById(R.id.lstItemName);
m_itemMapDrop = (ImageView) view.findViewById(R.id.lstItemMap);
}
}
#Override
public void onBindViewHolder(TrackedItemListAdapter.MyViewHolder holder, int position) {
holder.m_itemName.setText(obj.getName());
holder.m_itemMapDrop.setImageResource(R.drawable.ic_img_nomap);
holder.m_itemMapDrop.setOnClickListener(new View.OnClickListener() {
//#Override
public void onClick(View v) {
startMap();
}
});
}
}
m_trackedItemListAdapter = new TrackedItemListAdapter();
m_recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
m_recyclerView.setHasFixedSize(true);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
m_recyclerView.setLayoutManager(mLayoutManager);
m_recyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
m_recyclerView.setItemAnimator(new DefaultItemAnimator());
m_recyclerView.setAdapter(m_trackedItemListAdapter);
m_recyclerView.addOnItemTouchListener(new RecyclerTouchListener(getApplicationContext(), m_recyclerView, new RecyclerTouchListener.ClickListener() {
#Override
public void onClick(View view, int position) {
//a hack to turn off response if the imageview for location already responded
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
popupItemContent();
}
}, 200);
}
}
use this in your ConstraintLayout:
descendantFocusability="blocksDescendants"
I have already found this post that contains an answer how to do it. But there are some things that don't work and I can't figure out why.
#Override
public void onBindViewHolder(final ShipViewer.anAdapter.NumberViewHolder holder, final int position) {
final int[] mExpandedPosition = {-1};
final boolean isExpanded = position== mExpandedPosition[0];
holder.details.setVisibility(isExpanded?View.VISIBLE:View.GONE);
holder.itemView.setActivated(isExpanded);
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mExpandedPosition[0] = isExpanded ? -1:position;
TransitionManager.beginDelayedTransition(recyclerView);
notifyDataSetChanged();
}
});
holder.bind(position);
}
First of, I can't resolve details. Details is a gone ConstraintLayout inside of my Viewholder:
<android.support.constraint.ConstraintLayout
android:id="#+id/details"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="0dp"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#+id/bigtextname">
<TextView
android:id="#+id/textView10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView" />
</android.support.constraint.ConstraintLayout>
My second problem is that I can't resolve recyclerView. I have already tried inserting my adapter but that also doesn't work.
Lastly, is it fine that so many things are final here? This doesn't seem right, but if I don't do it it tells me that they need to be final to be accessed from an inner class.
My recyclerView:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/ShipRec"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/black">
</android.support.v7.widget.RecyclerView>
Initialization of RecyclerView:
View b = inflater.inflate(R.layout.shipviewerrecview, container, false);
if(!shipsCursor.moveToPosition(0)) {return b;}
shipropView = (RecyclerView) b.findViewById(R.id.ShipRec);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
shipropView.setLayoutManager(layoutManager);
shipropView.setHasFixedSize(true);
shippropAdapter = new ShipViewer.anAdapter(numberofField, Names, Type, Size, MaxSize, Amount);
ShipsDbHelper.close();
return b;
Probably expandable-recycler-view library from bignerdranch will useful for you.
I have a project i am developing but i have come to face a problem with a large gap appearing between two items on my android application ,i had tried to look on every previous suggestions here on how to solve the problem but none has worked for me
Here is my Layout that causes problems
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:layout_height="match_parent"
android:orientation="horizontal">
<android.support.v7.widget.RecyclerView
android:id="#+id/prodct_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="false"
android:scaleType="fitXY"
android:padding="0dp"
android:adjustViewBounds="true"/>
<android.support.design.widget.FloatingActionButton
android:id="#+id/actionbutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_gravity="end"
android:visibility="gone" />
And here is my oncreate method on activity associated with the above layout:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
tabletSize = getResources().getBoolean(R.bool.isTablet);
if (!tabletSize) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
setContentView(R.layout.order_details);
ab = getSupportActionBar();
ab.setDisplayUseLogoEnabled(true);
ab.setDisplayHomeAsUpEnabled(true);
actionButton = (FloatingActionButton) findViewById(R.id.actionbutton);
actionButton.setBackgroundColor(getResources().getColor(R.color.actionbar));
actionButton.setImageResource(R.drawable.ic_action_send);
appPreference = new AppPreference(this);
mRecyclerView = (RecyclerView) findViewById(R.id.prodct_list);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
final Intent intent = getIntent();
pyid = intent.getExtras().getLong(Quotes.ID);
mItemsDetail = QuoteItems.getItemsArray(this ,pyid);
mAdapter = new DeliveryDetailsAdapter(this, mItemsDetail);
mRecyclerView.setAdapter(mAdapter);
pContent = new ParseContent(this);
title = intent.getExtras().getString(Quotes.REFERENCE);
if (pyid == 0)
finish();
if (Validating.areSet(title))
ab.setTitle("Quote Details");
else
ab.setTitle(getString(R.string.strorder));
mRecyclerView.addOnItemTouchListener(
new RecylerItemClick(this, new RecylerItemClick.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
}
})
);
And here is my list of items populated on recyclerview:
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="#+id/card_view11"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/rltotals"
android:layout_below="#+id/autotxtsaleclientname"
card_view:cardBackgroundColor="#color/white"
card_view:cardCornerRadius="7.0dip"
card_view:cardElevation="0dp">
<android.support.v7.widget.RecyclerView
android:id="#+id/prodct_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:scrollbars="vertical" />
</android.support.v7.widget.CardView>
And when i run the application on that part the items on recyclerview displays with large gap between each item
Can anyone help me to solve the issue with the gap between the items
The problem is with your adapter class. You need to make the adapter class parent height as wrap_content. Since your recylerview is vertical, you need to change the height of the adapter parent layout to wrap_content. After that also if the problem exists check whether any other adapter child view height is match_parent, if so change that to wrap_content.
Hope this is Helpful :)
check the item layout inside DeliveryDetailsAdapter for inflating each item (OncreateViewholder)
You may have given some margin in root layout. remove margin from root layout and set width as match parent and height as wrap_content