How do I create a ListView with rounded corners in Android?
How do I create a ListView with rounded cor开发者_开发问答ners in Android?
Here is one way of doing it (Thanks to Android Documentation though!):
Add the following into a file (say customshape.xml) and then place it in (res/drawable/customshape.xml)
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#SomeGradientBeginColor"
android:endColor="#SomeGradientEndColor"
android:angle="270"/>
<corners
android:bottomRightRadius="7dp"
android:bottomLeftRadius="7dp"
android:topLeftRadius="7dp"
android:topRightRadius="7dp"/>
</shape>
Once you are done with creating this file, just set the background in one of the following ways:
Through Code:
listView.setBackgroundResource(R.drawable.customshape);
Through XML, just add the following attribute to the container (ex: LinearLayout or to any fields):
android:background="@drawable/customshape"
Hope someone finds it useful...
Although that did work, it took out the entire background colour as well. I was looking for a way to do just the border and just replace that XML layout code with this one and I was good to go!
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke android:width="4dp" android:color="#FF00FF00" />
<padding android:left="7dp" android:top="7dp"
android:right="7dp" android:bottom="7dp" />
<corners android:radius="4dp" />
</shape>
@kris-van-bael
For those having issues with selection highlight for the top and bottom row where the background rectangle shows up on selection you need to set the selector for your listview to transparent color.
listView.setSelector(R.color.transparent);
In color.xml just add the following -
<color name="transparent">#00000000</color>
Update
The solution these days is to use a CardView
with support for rounded corners built in.
Original answer*
Another way I found was to mask out your layout by drawing an image over the top of the layout. It might help you. Check out Android XML rounded clipped corners
The other answers are very useful, thanks to the authors!
But I could not see how to customise the rectangle when highlighting an item upon selection rather than disabling the highlighting @alvins @bharat dojeha.
The following works for me to create a rounded list view item container with no outline and a lighter grey when selected of the same shape:
Your xml needs to contain a selector such as e.g. ( in res/drawable/customshape.xml):
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_pressed="true" >
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<stroke android:width="8dp" android:color="@android:color/transparent" />
<padding android:left="14dp" android:top="14dp"
android:right="14dp" android:bottom="14dp" />
<corners android:radius="10dp" />
<gradient
android:startColor="@android:color/background_light"
android:endColor="@android:color/transparent"
android:angle="225"/>
</shape>
</item>
<item android:state_pressed="false">
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<stroke android:width="8dp" android:color="@android:color/transparent" />
<padding android:left="14dp" android:top="14dp"
android:right="14dp" android:bottom="14dp" />
<corners android:radius="10dp" />
<gradient
android:startColor="@android:color/darker_gray"
android:endColor="@android:color/transparent"
android:angle="225"/>
</shape>
</item>
Then you need to implement a list adapter and override the getView method to set the custom selector as background
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//snip
convertView.setBackgroundResource(R.drawable.customshape);
//snip
}
and need to also 'hide' the default selector rectangle e.g in onCreate (I also hide my thin grey divider line between the items):
listView.setSelector(android.R.color.transparent);
listview.setDivider(null);
This approach solves a general solution for drawables, not just ListViewItem with various selection states.
Yet another solution to selection highlight problems with first, and last items in the list:
Add padding to the top and bottom of your list background equal to or greater than the radius. This ensures the selection highlighting doesn't overlap with your corner curves.
This is the easiest solution when you need non-transparent selection highlighting.
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="@color/listbg" />
<stroke
android:width="2dip"
android:color="#D5D5D5" />
<corners android:radius="10dip" />
<!-- Make sure bottom and top padding match corner radius -->
<padding
android:bottom="10dip"
android:left="2dip"
android:right="2dip"
android:top="10dip" />
</shape>
actually, i think the best solution is described on this link:
http://blog.synyx.de/2011/11/android-listview-with-rounded-corners/
in short, it uses a different background for the top, middle and bottom items, so that the top and bottom ones would be rounded.
This was incredibly handy to me. I would like to suggest another workaround to perfectly highlight the rounded corners if you are using your own CustomAdapter
.
Defining XML Files
First of all, go inside your drawable folder and create 4 different shapes:
shape_top
<gradient android:startColor="#ffffff" android:endColor="#ffffff" android:angle="270"/> <corners android:topLeftRadius="10dp" android:topRightRadius="10dp"/>
shape_normal
<gradient android:startColor="#ffffff" android:endColor="#ffffff" android:angle="270"/> <corners android:topLeftRadius="10dp" android:topRightRadius="10dp"/>
shape_bottom
<gradient android:startColor="#ffffff" android:endColor="#ffffff" android:angle="270"/> <corners android:bottomRightRadius="10dp" android:bottomRightRadius="10dp"/>
shape_rounded
<gradient android:startColor="#ffffff" android:endColor="#ffffff" android:angle="270"/> <corners android:topLeftRadius="10dp" android:topRightRadius="10dp" android:bottomRightRadius="10dp" android:bottomRightRadius="10dp"/>
Now, create a different row layout for each shape, i.e. for shape_top
:
You can also do this programatically changing the background.
<TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginLeft="20dp" android:layout_marginRight="10dp" android:fontFamily="sans-serif-light" android:text="TextView" android:textSize="22dp" /> <TextView android:id="@+id/txtValue1" android:layout_width="match_parent" android:layout_height="48dp" android:textSize="22dp" android:layout_gravity="right|center" android:gravity="center|right" android:layout_marginLeft="20dp" android:layout_marginRight="35dp" android:text="Fix" android:scaleType="fitEnd" />
And define a selector for each shaped-list, i.e. for shape_top
:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Selected Item -->
<item android:state_selected="true"
android:drawable="@drawable/shape_top" />
<item android:state_activated="true"
android:drawable="@drawable/shape_top" />
<!-- Default Item -->
<item android:state_selected="false"
android:drawable="@android:color/transparent" />
</selector>
Change your CustomAdapter
Finally, define the layout options inside your CustomAdapter
:
if(position==0)
{
convertView = mInflater.inflate(R.layout.list_layout_top, null);
}
else
{
convertView = mInflater.inflate(R.layout.list_layout_normal, null);
}
if(position==getCount()-1)
{
convertView = mInflater.inflate(R.layout.list_layout_bottom, null);
}
if(getCount()==1)
{
convertView = mInflater.inflate(R.layout.list_layout_unique, null);
}
And that's done!
to make border u have to make another xml file with property of solid and corners in the drawable folder and calls it in background
I'm using a custom view that I layout on top of the other ones and that just draws the 4 small corners in the same color as the background. This works whatever the view contents are and does not allocate much memory.
public class RoundedCornersView extends View {
private float mRadius;
private int mColor = Color.WHITE;
private Paint mPaint;
private Path mPath;
public RoundedCornersView(Context context) {
super(context);
init();
}
public RoundedCornersView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.RoundedCornersView,
0, 0);
try {
setRadius(a.getDimension(R.styleable.RoundedCornersView_radius, 0));
setColor(a.getColor(R.styleable.RoundedCornersView_cornersColor, Color.WHITE));
} finally {
a.recycle();
}
}
private void init() {
setColor(mColor);
setRadius(mRadius);
}
private void setColor(int color) {
mColor = color;
mPaint = new Paint();
mPaint.setColor(mColor);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setAntiAlias(true);
invalidate();
}
private void setRadius(float radius) {
mRadius = radius;
RectF r = new RectF(0, 0, 2 * mRadius, 2 * mRadius);
mPath = new Path();
mPath.moveTo(0,0);
mPath.lineTo(0, mRadius);
mPath.arcTo(r, 180, 90);
mPath.lineTo(0,0);
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
/*Paint paint = new Paint();
paint.setColor(Color.RED);
canvas.drawRect(0, 0, mRadius, mRadius, paint);*/
int w = getWidth();
int h = getHeight();
canvas.drawPath(mPath, mPaint);
canvas.save();
canvas.translate(w, 0);
canvas.rotate(90);
canvas.drawPath(mPath, mPaint);
canvas.restore();
canvas.save();
canvas.translate(w, h);
canvas.rotate(180);
canvas.drawPath(mPath, mPaint);
canvas.restore();
canvas.translate(0, h);
canvas.rotate(270);
canvas.drawPath(mPath, mPaint);
}
}
There are different ways to achieve it. The latest approach is using CardView for each ListItem component. Here are some steps.
- Create a layout resource file; let's name it "listitem.xml
- Copy and paste the under enclosed Listitem.xml layout body into it.
- Create RowItem class for each listitem data; later you will instantiate this to assign values for each list item. Check Code below, RowItem.class.
- Create a custom ListAdapter; let's name it ListAdapter.class, and inflate this (#1) list item layout for each list item (Check the second code snippet for this one)
- Set this adapter (#3) the way you set default adapters inside an activity the listview belongs to. maybe the only difference would be you first have to instantiate RowItem class with values and add RowItem object to your adapter then notify your adapter that the data is changed.
**listitem.xml**
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alignmentMode="alignMargins"
android:columnCount="1"
android:columnOrderPreserved="false"
android:rowCount="1">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_rowWeight="1"
android:layout_columnWeight="1"
android:layout_margin="6dp"
app:cardCornerRadius="8dp"
app:cardElevation="6dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<ImageView
android:id="@+id/sampleiconimageID"
android:layout_width="60dp"
android:layout_height="60dp"
android:padding="5dp"/>
<LinearLayout android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/titleoflistview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Main Heading"
android:textStyle="bold" />
<TextView
android:id="@+id/samplesubtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sub Heading"
/>
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
</GridLayout>
</LinearLayout>
RowItem.Class
public class RowItem {
private String heading;
private String subHeading;
private int smallImageName;
private String datetime;
private int count;
public void setHeading( String theHeading ) {
this.heading = theHeading;
}
public String getHeading() {
return this.heading;
}
public void setSubHeading( String theSubHeading ) {
this.subHeading = theSubHeading;
}
public String getSubHeading( ) {
return this.subHeading;
}
public void setSmallImageName(int smallName) {
this.smallImageName = smallName;
}
public int getSmallImageName() {
return this.smallImageName;
}
public void setDate(String datetime) {
this.datetime = datetime;
}
public String getDate() {
return this.datetime;
}
public void setCount(int count) {
this.count = count;
}
public int getCount() {
return this.count;
}
}
Sample ListAdapter
public class ListAdapter extends BaseAdapter {
private ArrayList<RowItem> singleRow;
private LayoutInflater thisInflater;
public ListAdapter(Context context, ArrayList<RowItem> aRow){
this.singleRow = aRow;
thisInflater = ( LayoutInflater.from(context) );
}
@Override
public int getCount() {
return singleRow.size(); }
@Override
public Object getItem(int position) {
return singleRow.get( position ); }
@Override
public long getItemId(int position) {
return position;
}
public View getView(int position, View view, ViewGroup parent) {
if (view == null) {
view = thisInflater.inflate( R.layout.mylist2, parent, false );
//set listview objects here
//example
TextView titleText = (TextView) view.findViewById(R.id.titleoflistview);
RowItem currentRow = (RowItem) getItem(position);
titleText.setText( currentRow.getHeading() );
}
return view;
// LayoutInflater inflater=.getLayoutInflater();
// View rowView=inflater.inflate(R.layout.mylist, null,true);
//
// titleText.setText(maintitle[position]);
// subtitleText.setText(subtitle[position]);
// return null;
};
}
精彩评论