I know loads of articles describe how to animate the expansion/collapse of a listView item but I simply cannot get it working :-(
I have a listView where each item contains a hidden LinearLayout element ll (View.GONE) and I would like to expand/collapse ll using an animation. My problem is - apparently - the variable height of ll. ll's height is set to wrap_content as it contains a description and some images.
The challenge as I see it it the fact that I start out with ll's viibility set to View.GONE. If I change it to View.VISIBLE before starting the animation the area expands instantly and the text animates down. If I change visibility after the animation I don't get the animation effect.
Here's a snippet from my onItemClickListener:
LinearLayout llMatchInfo = (LinearLayout) view.findViewById(R.id.matchInfo);
//llMatchInfo.setVisibility(llMatchInfo.getVisibility() == View.GONE ? View.VISIBLE : View.GONE);
Animation animation = AnimationUtils.loadAnimation(ctx, (llMatchInfo.getHeight() == 0) ? R.anim.down_from_top : R.anim.up_from_bottom);
llMatchInfo.startAnimation(animation);
Can anyone shed some light as to how to accomplish animating an element with height wrap_content and initial visibility View.GONE?
I'm really sorry if I've missed an obvious answer but I just feel like I've tried everything... :-/
Thanks in advance
You can use this library:
https://github.com/traex/ExpandableLayout
It can save your life and time.
It's easy, you need only add the custom listview on your layout, create the adapter with customs implementations, setAdapter and voalá.
I just tried to help...
Related
What I intend to achieve
The item view should occupy the entire height of the item
It could be that the item height is lesser than the height of the tallest item in the recyclerview, in which case it should just stick to the top like in the screenshot above.
The bug I'm running into
As in the screenshot above, views are getting truncated.
What I've tried so far
Initially I went with wrap_content on the recyclerview, now that it is supported. It didn't work when none of the views visible on the screen at the time were the tallest. This makes sense in how the view hierarchy is laid out. How can the height of something which hasn't even been bound to any data yet be calculated if the height is dependent on that data?
Workaround time :S
Instead of trying a custom layoutmanager, I first went with what I felt needed to be done - laying out all item views at the beginning to figure out their height.
There's a progressbar and an animation playing in the upper part of the screen to catch the user's attention while all this happens with recyclerview visibility set to invisible. I use two things, one didn't suffice - I've attached an observer in the adapter's onViewAttached() call and I've used a scroll change listener as well. There's a LinearSnapHelper attached to the recycler view to snap to adjacent (next or previous, depending on the scroll direction) position on scroll.
In this setup,
I'm going to each position in the recyclerview using layoutManager.smoothScrollToPosition()
Getting the child view height using
View currentChildView = binding.nextRv.getChildAt(layoutManager.findFirstCompletelyVisibleItemPosition());
if (currentChildView != null) {
currentChildHeight = currentChildView.getHeight();
}
in scroll change listener on RecyclerView.SCROLL_STATE_IDLE or by passing the height to the view attached observer mentioned above in the adapter's onViewAttachedToWindow()
#Override
public void onViewAttachedToWindow(BindingViewHolder holder) {
if (mObserver != null) {
mObserver.onViewAttached(holder.binding.getRoot().getHeight());
}
}
Storing a maxHeight that changes to the max of maxHeight and new child's height.
As is evident, this is ugly. Plus it doesn't give me the current view's height - onAttached means it's only just attached, not measured and laid out. It is the recycled view, not the view bound to current data item. Which presents problems like the truncation of view illustrated above.
I've also tried wrap_content height on the recycler view and invalidating from recycler's parent till the recycler and the child on scroll coming to SCROLL_STATE_IDLE. Doesn't work.
I'm not sure how a custom layoutmanager can help here.
Can someone guide me in the right direction?
I could not accept #Pradeep Kumar Kushwaha's answer because against one solution, I do not want different font sizes in the list. Consistency is a key element in design. Second alternative he gave couldn't work because with ellipsize I would need to give a "more" button of some sort for user to read the entire content and my text view is already taking a click action. Putting more some place else would again not be good design.
Changing the design with the simple compromise of resizing the recyclerview when the tallest, truncated item comes into focus, it turns into the simple use case of notifyItemChanged(). Even for the attempt I made using the view attached observer and scroll state listener, notifyItemChanged could be used but that approach is just too hacky. This I can live with in both code and design. Here goes the code required.
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
int position = ((LinearLayoutManager) binding.nextRv.getLayoutManager())
.findFirstVisibleItemPosition();
if (position != nextSnippetAdapter.getItemCount() - 1) {
binding.nextRv.getAdapter().notifyItemRangeChanged(position, 2);
} else {
binding.nextRv.getAdapter().notifyItemChanged(position);
}
}
}
For my particular setup, calling for just these two elements works. It can further be optimized so as to call for single element at position + 1 in most cases, and checking and calling for the appropriate one in corner (literal) cases.
Inside your adapter where I can find two cards one on top and another on bottom
How I would have defined my layout is like this:
Cardview1
LinearLayout1 --> orientation vertical
cardview2 (Top card where text is written)
Linearlayout2 (where I can see icons such as like etc)-->orientation horizontal
Now fix the height of Linearlayout2 by setting it to wrap content.
And the height of cardview2 should be 0dp and add weight = 1
Now inside cardview2 add a TextView1 to matchparent in height and width.
Better inside textview1 add ellipsize to end and add max lines
If you want to show all lines try to find autoresizetextview library it can be founded here --> AutoResizeTextView
Hope it helps.
I think the recyclerview can be set to height wrap_content. And the items can be make like height to match_parent.
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layput_height="wrap_content"/>
Item as:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
// your coode
</androidx.constraintlayout.widget.ConstraintLayout>
I had little more requirement than the question. Even my problem solved in the way.
Remember I am using:
androidx.recyclerview:recyclerview:1.0.0-beta01
dependency for the project
Resume:
I have a ChatActivity that's basic a RecyclerView. In my chat_row_layout, i have a horizontal LinearLayout (width = wrap_content) with 2 TextViews, one of then with layout_weight = 1.
My problem :
When scrolling the recycler, some of my views width get messed up because of the recycling.
Is there some way to fix that? Ex. Force the adapter to get the original view measures
Ps: sorry for the bad formating + lack of info (i'm on phone)
Below is the item of layout i am using in listview.The issue is that when i make visibility of countlayout gone in some items of list I expect vibelayout to be exactly at same place where countlayout was since weigtSum is 3.But it is appearing slightly right of position where countlayout was.
I am using View.GONE so that countlayout space is assigned to vibelayout but its not happening.. vibelayout is appearing slightly right of position where countlayout was.
Try using android:layout_width="0dp" in all 3 layouts. It will solve your problem
I have a horizontal scroll view, I also have a relativelayout as it's child. I am adding child views of this relativelayout dynamically. I have a header text which should be update when I scroll according to respective child views. How can I do this because I am able to get the current focused item in horizontal scroll. Please give me some suggestion or examples which can be helpful for me, thanks..
You should specify an OnTouchListener for your HorizontalScrollView and in it's onTouch() method detect the type of MotionEvent and change your TextView's color to the appropriate
If you are creating this childs dinamically you can set a tag to them with the content you want to show in the header TextView.
//Creating RelativeLayout childs
TextView newChild = new TextView(this).
newChild.setTag(textToShowWhenThisItemIsFocused);
Then if you know which item is focused you just have to get the tag.
// "selected" is the focused view
header.setText((String) selected.getTag());
When to use the second code depends on your implementation. Since you didn't provide any code it's hard to know how to monitor the scroll, but i.e. you could control Touch Events and update the header when the user is moving his finger over the screen (you should also take into account the inertia after the user stops tapping).
EDIT: How to get the focused View
First of all, I barely have experience doing things like this, so I'm not sure if this is going to work or if there are better ways to do it. I'll just tell you the way I would approach this problem.
To be able to know the focused View you need to know the coordinates where this View should be. Since it's an horizontal ScrollView we will need the X coordinate. Since we want the View in the middle of the ScrollView I would do it like this:
private int centerSV;
private ScrollView mScrollView;
...
centerSV = mScrollView.getWidth()/2;
Now we have the center of the ScrollView. Now we need to know which child is in this position:
private int getFocusedChildId(){
for(int i=0; i<mChilds.length; i++){
int childLeftCoord = mChilds[i].getLeft() - mScrollView.getScrollX();
if(childLeftCoord <= centerSV && centerSV <= childLeftCoord + mChilds[i].getWidth())
return mChilds[i].getId();
}
// No view found in the center, maybe ScrollView wasn't full. Return the first one
return mChilds[0].getId();
}
Again, I'm not sure if this is going to work, it's just an idea of how to approach your issue. Also, you should take this into account:
getWidth() and getHeight() of View returns 0
I use a checkable listview ( I've set : Mylistview.setChoiceMode(1)).
Inside each row I have 2 ImageViews and 2 TextViews.
I'd like that when my item is checked, the text inside the TextViews scrolls if the text is too long. So in the XML of my row, I've assign the following to both of my TextViews :
android:singleLine="true"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
Unfortunately, this will make the text scroll only if the item is focused and not checked.
So the solution I think about are :
1) Make my own Custom TextView class and make it focus itself when checked (I don't know if it is possible ?)
2) Put the focus to the checked item even if we're in touch mode (use SetFocusableInTouchMode)
I haven't achieved to do the 2nd solution and I don't know which method override if I want to make my own own Custom TextView.
Could you help me to make the text scroll when the item is checked ? Thanks.
You need to do something like following.
int scrollAmount = mTextView.getLayout().getLineTop(mTextView.getLineCount())
-mTextView.getHeight();
// if there is no need to scroll, scrollAmount will be <=0
if(scrollAmount>0)
mTextView.scrollTo(0, scrollAmount);
else
mTextView.scrollTo(0,0);