I have a RecyclerView that display the list of modifications per day for a document. Items are TextView with values like "Line 3 updated to ...", "Line 5 removed", etc. The ViewHolder handle 2 types of views :
Headers : which are the days. These are simple TextView too that are in bold, larger, etc.
Logs: that's what I was talking about : "Line 3 updated ...", etc.
What I would like is that each "day", with its corresponding logs are embedded inside a CardView. But a document can have huge number of modifications per day (>100). So programmatically creating a layout with a CardView as the root, calling 100 times addView() on it to add each logs and then passing this layout to the RecyclerView does not seems a good idea to me.
Is their any way to delimit between a "positionStart" and a "positionEnd" views that will be embedded inside a CardView? It seems to me that this isn't possible or by adding each TextView programmatically inside a CardView but it will then slow down the binding of the views and break the ViewHolder pattern. Am I right or is their a solution I didn't think about ?
You have 3 options to achieve this "grouped in a card" behavior.
(as mentioned) you create the layout yourself and put the whole thing into the recyclerview. This is probably the worst solution since it negates the idea of a recyclerview in the first place.
You just wrap each of your items in a CardView (and set the corner radius to 0dp).
On < 21 devices (I think) there will be some additional padding and every item will appear as its own card, but on higher API versions those cards will lie next to each other and just have some "seam" between them. The shadow on the corner is also a bit buggy, but this is probably the easiest and cheapest solution.
Alternatively you can also create a custom view that fixes the errors mentioned above (margins between and shadow) and use your own to wrap the views. (I believe this is what the Inbox app does if I recall correctly, which also features lists in cards.)
You use an ItemDecoration. For this approach you need a kind of stable setup of your dataset, but if its just the headers and logs, you can draw a shadow above the header, draw borders to the left and right of every item, and draw a shadow beneath the last log. This will also require some setup, and if you introduce further view types you will also have to modify this code (it's highly dependent on your data set)
The 1. method is probably the worst idea. It will work for small lists.
The 2. method can work, but you either will have to create your own custom view or live with a "bugged" version on lower api levels.
The 3. method is something I tried once for fun, and will work, but you will have some additional dependency between your data, your adapter, and your decoration. You can see an example of this decoration here on GitHub. It just draws a shadow around all of the items.
Related
I'm a flutter user trying out kotlin dev first time, but im kinda confused what the best alternatives for this type of layouts are.
I understand that I use recyclerview for a dynamic number of items. And since theres two dynamic ones (one for number of sets, one for each exercise). Do I use two nested recyclerviews?
It really depends what you're doing here. A RecyclerView is basically a list, so it's a good choice for a scrollable set of items - which it looks like your Exercises are. But how do you want to display those weights?
It's possible to make them scrollable, so some of them aren't visible on the screen (the categories on the Play Store act like this). For that you would need a nested scrolling view of some kind - could be a horizontal RecyclerView, or it could just be a ScrollView wrapped around a LinearLayout you throw views into.
But the other option you might want, is that for each Exercise, all of its weights are visible in a grid of some kind. No scrolling, all there up-front to see and poke at. So the first question is which of those do you want?
I'd assume it's this version - where you can easily visualise the contents - in which case you're not talking about nested lists, it's just one list, and each item contains a grid you can add things too. For that setup, there's GridLayout, TableLayout, and ConstraintLayout Flow (which acts like FlexBox if you're familiar with that). So in the layout for an item in your list, you have a container for stuff, and you put the stuff in it, and the container expands vertically as needed
Is there another way how to group elements of RecyclerView with the same view type inside a card? As an example, I want to achieve something like in Revolut. What I've read until now is to use a list inside a recycler view, but I am not sure about the performance, I might show more than 10 000 items.
Happy that I managed to do it.
If anyone is interested in how I managed to pull this off, I just created a recycler view with two view types and the one with the "transactions" I made it a card with a negative margin-top. Basically I have set marginTop to -16dp so the card corners don't overlap. Caution, you need to set a bigger margin if the corner radius is bigger than 8 (I used 8)
I will answer happily on the questions
Good luck implementing it.
This is not a design question, I have the item designed. I am confused on how to pull it off.
I am tasked with designing a view for Android that has a view of a user's post and the comments under it. The post contains some extra information and widely different design from the comments, but all of them need to scroll in tandem, like a web page would (Oh, how I am spoiled by my years of web dev...).
My current solution is to nest a LinearLayout (at the top of the view to contain the user's post) and a RecyclerView (below the post to display the comments) inside a vertical ScrollView. The information is actually displayed, but the RecyclerView, of course, scrolls independently of the LinearLayout above it and ruins the functionality of the view.
I wish, if possible, to keep the RecyclerView in use
The best case scenario would be to have the LinearLayout with the post scroll a certain amount, and then the RecyclerView take over. However, I don't want to poison my codebase with 200+ ugly lines of code to achieve this, so if this is a laborious task to complete, I would rather look for alternatives.
The first thing to understand is: do you really need a RecyclerView, or even better, do you really need recycling?
If the answer is yes, the way to go is two different item types in the RecyclerView's Adapter (for more details, see here or here). This concept was already present in the ListView: the main difference is that RecyclerView enforce the use of the View Holder pattern. It is not so complex, and, more importantly, is the way the RecyclerView is supposed to solve that problem. Depending on your UI design, you may also want to have different view types for different types of comments (plain text, images, ...). Remember that, when the RecyclerView is included in a ScrollView, the recycling won't work, because all the items in it will be drawn at once to compute the height of the content of the parent ScrollView.
If the answer is no, then you could just create your views at runtime and add them to a parent LinearLayout in a ScrollView. It is really nothing more than a for loop.
A more fancy approach would be to use an ItemDecoration for the user's post, but I don't see any specific advantage in this case.
I want to implement a schedule-like overview as List and as Grid View. Currently this works using a RecyclerView and swapping out the LayoutManager at Runtime. This works fine in general, but as soon as I tuned the grid cells to have a fixed height, I realised that the RecyclerView does not respect the wrap_content layout property and instead introduces whitespace between my grid items.
I then stumbled over more or less the same question for the linear LayoutManager and the third-party provided specialisations of the LayoutManager. A quick searched revealed nothing similar for the GridLayoutManager and I am beginning to suspect I am going down the wrong road:
Apart from the easy switching between layouts, there seems to be no reason for me to work with a recycler view:
I dont't require any special animation stuff.
My adapter returns a fixed list without loading more items on demand.
Actual recycling of items doesn't even take place.
So I am currently evaluating to simply switch to separate Fragments using an ordinary ListView and GridView. But this does feel a little like working against the intentions of Android ... After all, they introduced a single view that (in theory) should exactly do what I need.
Am I missing something obvious that could turn out to be painful when using those "legacy" Views to get the job done? Or are there maybe other, more specific Views that allow switching from linear to grid layouts apart from the RecyclerView?
If this should be of importance: I am using Xamarin.Android, not native Java. But I would be willing to port some code from Java to C#.
Follow this ticket:
https://code.google.com/p/android/issues/detail?id=74772
It is not merged yet but soon should be merged and WRAP_CONTENT support should be available in the next version (23.1.1+) or the one after.
I am interested in creating a ListView where each row is marked the way it is done in GMail for 3.0+. This creates a nice separation of the left and right ListFragment.
Other examples include also Google Calendar on 2.3.4 for instance where a color marker is on the left of the ListView.
See the grey vertical divider between the two lists. How does one achive something like this? A bonus would be also the alternating width, but I guess that is only a smaller layout change.
I know I could probably do something like inserting an ImageView in there and then fill it with the color I would like but it seems to me that this is an ugly hack.
Another question would be also if there is a generalized way to combine the two ListView fragments somehow the way the GMail or Mail applications do it.
If you want speed, then the option I would go for is to use a custom View class (e.g. extend RelativeLayout) for the row container View and override the dispatchDraw(Canvas canvas) method.
The dispatchDraw method is called after the View has drawn its own contents and before it draws its children - the children are drawn when you call super.dispatchDraw.
Use this to do something like
private boolean mDrawMarker = false;
public void setShouldDrawMarker(boolean drawMarker) {
mDrawMarker = drawMarker;
}
public boolean getShouldDrawMarker() {
return mDrawMarker;
}
#Override
public void dispatchDraw(Canvas canvas) {
// draw the children of our view
super.dispatchDraw(canvas);
// draw our marker on top of the children if needed
if (mDrawMarker) {
// e.g. canvas.drawRect(...) or canvas.drawBitmap(...)
}
}
This way you avoid adding any extra views to the hierarchy which means you won't incur any penalty in the layout or measuring phases. Remember to re-use Paint and Rect objects if drawing a rectangle rather than creating a new one each time. Similarly if you use a bitmap you should share the same Bitmap instance across all instances of your View rather than loading a new one from your resources each time (this does not mean putting them in static fields)
For the indentation of the items, since in this case the lists don't seem to be overlapping you could (off the top of my head):
Set a left margin on the row container (not totally sure this will work)
Wrap the row container in a LinearLayout and set the left padding on this (if the above doesn't work)
Use a custom view class (if setting the left margin doesn't work)
Go with #commonsware suggestion and use two Views - one on the left with the grey background color and another to the right of that with the marker color - then just set the view on the left to visible/gone if you want indentation/no-indentation
As for the overlapping of the Views in the second example, I'll defer to #commonsware answer.
Please tell me if I have understood the problem correctly.... You would like to mark certain rows as selected, and selected rows appear to be indented visually (with a different colour and margin)?
Here are 2 techniques:
1 - Using a StateListDrawable for the row background:
If so I would create a list row layout file and set the "background" property to a StateListDrawable (can be XML). This will allow the row to switch visual states for selected and not selected.
The "drawable" attribute of the StateListDrawable would be a 9-patch PNG, one for the un-selected state that includes no margin, and one for the selected... the selected one would define a margin within the PNG itself, by specifying the content-area/bottom black line to not extend fully to the left hand side of the PNG, leaving a region which is your unscaled margin.
For the benefit of people finding this, Radley Marx just posted an excellent post on 9 patch: http://radleymarx.com/blog/simple-guide-to-9-patch/
With ListViews it's sometimes the case you want to turn off the "listSelector" (which is a separate entity either rendered above the list, or behind) and instead use the "duplicateParentState" attribute to allow the row itself to display the selection (no list selector needed). This can provide a bit more creative freedom, especially when you want to have variable-width margins on certain rows, or several types of row that all look different. Totally depends on the design though.
2 - Using a margin for each row:
If you decided you needed multiple types of colour indicator and so on, you may have to use a different approach, providing a margin attribute (which probably won't just work straight away)... This relates to how LayoutParams are used by the layout system. I'm trying to remember the exact details, but I think this is due to the different types of LayoutParam subclass, and the properties of a MarginLayoutParam (or subclass of that) e.g. marginLeft may be ignored by the layout code of the ListView. You should be using an instance of AbsListView.LayoutParams, which includes no options for margins. One way is to nest your row inside a container View (subclass) which does allow for margins in its LayoutParams*. I'm certain I didn't end up doing this extraneous-nesting but I'll have to dig into some code to remember the better solution.
You mention putting an ImageView and filling it with colour. There are a couple of alternatives you could look at... The most performant would probably be to define your own ListRow class and use onDraw() to actually draw the row contents yourself, canvas.draw_xyz() to paint the little colour tab, and draw text etc for the rest, rather than build the row in a composite layout. The second method using layouts would be to have a lighter-weight <View layout_height="match_parent" layout_width="4dip" background="#ffff0000" /> for example.
*A golden rule in Android layout: Complex UI hierarchy is death to performance, especially with things like ListView. It's often possible to avoid this by using other things: RelativeLayout, drawableTop(etc), 9-patch images, rather than adding more Views.
If I have misunderstood and the above is just too basic please could you provide some more detail, maybe a diagram indicating the exact part you need to reproduce.
How does one achive something like this?
Off the cuff, I'd use a View with the desired background color, visible when you want it, invisible when you don't.
A bonus would be also the alternating width, but I guess that is only a smaller layout change.
I do not know what you mean by "alternating width". I think there are two Views with the desired background color in a horizontal LinearLayout, with only one visible.
I know I could probably do something like inserting an ImageView in there and then fill it with the color I would like but it seems to me that this is an ugly hack.
Well, ImageView is a bit heavier than is needed. Otherwise, I fail to see why this is a hack. Think of them as icons that simply happen to be tall, thin, gray, and not always needed.
Another question would be also if there is a generalized way to combine the two ListView fragments somehow the way the GMail or Mail applications do it.
Use a RelativeLayout, so the right-hand fragment can float over the left-hand fragment.