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 开发者_如何学Gogrey 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.
精彩评论