I'm using the TwoWayView library : https://github.com/lucasr/twoway-view to try and implement a horizontal ListView of sorts to overhaul what I had before. (HorizontalScrollView with a horizontally oriented LinearLayout that I add views to)
I want to have a margin before the first item, so there's a blank space at the left when the View is first created. But when scrolled, the blank space would be scrolled and disappear as well. When using a normal ListView it is essentially a HeaderView that I want.
When I implemented the scroll using HorizontalScrollView, I simply programmatically checked the first item and added the margin, which worked since its parent was LinearLayout and accepts margins. But I cannot do this in the getView() of the adapter used for this AdapterView since its LayoutParams do not inherit ViewGroup.MarginLayoutParams
I've also tried setting clipToPadding="false", but the views gets recycled too early, which is unacceptable since the padding I need is noticeably big.
Is there a way to achieve this behavior without moving all the HeaderView code from ListView into the TwoWayView library?
You can try something like this:
on getView(int pos, View convertView, ViewGroup parent){}
{
if (convertView == null)
{ ... }
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT , ViewGroup.LayoutParams.WRAP_CONTENT);
/// To set your margin, you just need to test the position :)
params.setMargins(left, top, right, bottom);
viewHolder.LinearLayout.setLayoutParams(params);
}
Hope this would help you.
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
I'm working with TextViews progammatically and I need to dynamically add new views and set their left/top position in the RelativeLayout parent.
What I'm doing is something like this:
RelativeLayout global=(RelativeLayout)findViewById(R.id.global);
TextView view=(TextView)findViewById(R.id.root);
RelativeLayout.LayoutParams lp=new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
final TextView childView=new TextView(view.getContext());
lp.addRule(RelativeLayout.ABOVE, view.getId());
childView.setLayoutParams(lp);
childView.setId(childView.getId()+1);
childView.setText("TRY STRING");
childView.setTextSize(view.getTextSize());
childView.setWidth(view.getWidth());
childView.setLeft(view.getLeft()+interval);
global.addView(childView);
Basically, I have a textview in a certain position (which is called view) and I'm trying to create a new textview above the existing one and on the same left position.
Although the getLeft() method correctly returns the left position of view, childView happens to have the left field set to zero, and appears at the left end of the screen when i add it to the RelativeLayout.
It looks like it's completely ignoring the setLeft method!
Can anyone explain why?
Thank you very much in advance!
public final void setLeft (int left)
Added in API level 11
Sets the left position of this view relative to its parent. This method is meant to be called by the layout system and should not generally be called otherwise, because the property may be changed at any time by the layout.
Parameters
left The bottom of this view, in pixels.
most likely the left value is being set by the layout every time it is drawn and overrides the value that you are setting on your view.
android layouts are very XML ish even when doing it programmatically.
try using
lp.setMargins(int left, int top, int right, int bottom);
..or some other method that won't be undone when the layout is drawn
I have a header view in a ListView I want to hide when not in use. I've included code that sets its visibility to View.GONE or View.VISIBLE depending on another control.
Unfortunately, the view I am trying to show and hide is laying out as though I were setting its visibility to View.INVISIBLE—that is, the ListView is allowing space for it even when it's hidden. How can I prevent this?
I have tried calling requestLayout() and it hasn't had any effect.
I am going to try one of the suggested solutions here:
Hide footer view in ListView?
namely, wrapping my header in a FrameLayout and letting the FrameLayout handle the layout change, not the ListView.
EDIT: I have found that basically the same bug occurs when I added a ViewPager subclass in which I had overridden onMeasure(int widthMeasureSpec, int heightMeasureSpec) as a header view: it was designed to lay itself out again when children were added, but the ListView displayed it wrongly. As a child of a LinearLayout, however, this worked fine.
I have added a FrameLayout as a parent view of the header tile I wish to hide. This means that when I hide the tile, the FrameLayout shrinks to fit it, and reports a height of zero to the ListView, which also shrinks. This is a pretty effective workaround, though a little weird.
Don't use View.VISIBLE or View.GONE for Showing and Hiding ListView Header and Footer.
Go Through removeHeaderView for help.
View v="YOUR VIEW";//Assume
You can remove Header like
your_list.removeHeaderView(v)
And you can add
your_list.addHeaderView(v);
Hope this help you.
That was my original code which was not working:
wv = new WebView(getActivity());
AbsListView.LayoutParams params = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, AbsListView.LayoutParams.WRAP_CONTENT);
wv.setLayoutParams(params);
...
((ListView) mainListView).addHeaderView(wv);
I have placed my webview to framelayout and added this framelayout to listview with addHeaderView
FrameLayout fl = new FrameLayout(getActivity());
AbsListView.LayoutParams paramsFl = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, AbsListView.LayoutParams.WRAP_CONTENT);
fl.setLayoutParams(paramsFl);
wv = new WebView(getActivity());
AbsListView.LayoutParams params = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, AbsListView.LayoutParams.WRAP_CONTENT);
wv.setLayoutParams(params);
...
fl.addView(wv);
((ListView) mainListView).addHeaderView(fl);
after that when I set visibility of my webview to GONE header became not visible.
I have a custom view which will be jar'ed up and added into another project. In the view I want to give an option of a button.
Here is what I have in the CustomView class.
final CustomView currentView = (CustomView) findViewById(this.getId());
RelativeLayout.LayoutParams params = (new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,RelativeLayout.LayoutParams.WRAP_CONTENT ));
params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
closeButton.setLayoutParams(params);
currentView.addView(closeButton);
This is all wrapped in a RelativeLayout Tag as well as the other objects of the application
Everything compiles however in the CustomView the Button is aligning left instead of right.Any Ideas???
I would guess the problem is your CustomView. It probably doesn't take the entire width of the window, and is just wide enough to fill its children (which, in your case, is the close button). Make sure your CustomView has a fill_parent horizontal layout.
Since your CustomView extends WebView, which, in turn, extends AbsoluteLayout, you can't expect it to handle RelativeLayout's parameters. Instead, it's best you put your customview and your close button inside a RelativeLayout and position them properly.
When adding your closeButton to your currentView you need to supply the LayoutParams as an argument as well in order for them to take effect.
Basically, switch
currentView.addView(closeButton);
with
currentView.addView(closeButton, params);
Since the width of your button is set to wrap_content, you could also try setting its layout_gravity to right.
params.gravity = Gravity.RIGHT;
I have a HorzontalScrollView with a LinearLayout inside. During Runtime I can add more LinearLayouts to the LinearLayout.
Now I have the problem that the Scrollview only scrolls a little bit and not smooth with one finger slide!
Does anyone have a solution for this problem?
HorizontalScrollView doesn't use an adapter that manages the list's memory, therefore it can't handle heavy (images, custom views, etc) lists.
You can use this Horizontal ListView http://www.dev-smart.com/archives/34 but make sure you don't write the on list item click method inside the getView, it will make the list scroll slow. Other than that, that's a great resource for a smooth horizontal list view.
You can also explore the Android view pager, which is also supported on lower Android versions using the compatibility pack: http://developer.android.com/sdk/compatibility-library.html
Edit - something like that in the adapter that inflates the XML you want (the linearLayout) and then populates every view with the relevant data.
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.chat_friends_list_item, null);
}
ImageView status = (ImageView)convertView.findViewById(R.id.status);
ImageView image = (ImageView)convertView.findViewById(R.id.image);
ImageView imageBorder = (ImageView)convertView.findViewById(R.id.image_border);
TextView title = (TextView)convertView.findViewById(R.id.title);
}
The problem was my parent Viewflow because it has stolen the swipe event! The HorizontalListView is too buggy for me! (Problems with size attributes)
However,thank you for your answer! ;)