您的位置:首页 > 移动开发 > Android开发

android如何给整个视图view圆角显示

2016-07-23 18:25 555 查看
关于如何给一个view设置圆角,有哪些方法呢?

1.给该view设置一个圆角的背景,这是最常见的用法。如以下代码:

round_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<corners android:radius="40dp" />
<solid android:color="#33ff0000" />
</shape>


这是给view设置40dp的圆角

<android.support.v7.widget.RecyclerView
android:id="@+id/rv_list_1"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_margin="20dp"
android:background="@drawable/round_bg" />


效果如图:

这种方式,正常情况下是没有问题的,可是如果想实现无缝的圆角显示,例如,它里面的一个列表,紧贴着边缘,即padding为0,会发现,圆角的那部分区域,还是有子view在显示的,并没有完全的实现真正的圆角,因为只是外层的背景圆角了而已。

那有什么办法让子view也圆角显示呢?下面说下第二种方法。

2.通过代码设置圆角,如下:

/**
* 设置视图裁剪的圆角半径
* @param radius
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static void setClipViewCornerRadius(View view, final int radius){

if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP){
//不支持5.0版本以下的系统
return;
}

if(view == null) return;

if(radius <= 0){
return;
}

/*view.setOutlineProvider(new ViewOutlineProvider() {

@Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom(), radius);
}
});*/
view.setOutlineProvider(new ViewOutlineProvider() {

@Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(0, 0, view.getWidth(),   view.getHeight(), radius);
}
});
view.setClipToOutline(true);

}


这是5.0版本之后新增的设置view边缘的方法,不支持5.0以下版本。

那有没更合适的方法呢,所幸,5.0版本之后,增加了新的组件,CardView,它可以设置圆角,阴影等。

<android.support.v7.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_margin="20dp"
app:cardBackgroundColor="#33ff0000"
app:cardCornerRadius="30dp">

<android.support.v7.widget.RecyclerView
android:id="@+id/rv_list_2"
android:layout_width="150dp"
android:layout_height="150dp" />
</android.support.v7.widget.CardView>


CardView提供了兼容版本,当5.0版本以上时,子view被圆角了, 那么5.0版本以上时,支持吗?经测试是不支持的,但是系统给它增加padding时子 view完全显示在布局里面,不会超出圆角部分,算是适配的方法,这样比上一种方法,更加合适了一点。

那还有没有更好的方法,真正解决圆角view和里面的子view,并且兼容5.0以下版本呢?答案也是有的。我们知道View的绘图时有调用draw方法,绘图机制可参考http://blog.csdn.net/leehong2005/article/details/7307456

View#draw方法,提供了一个最基本的绘制机制,子类通常不需要重写这个方法。我们可以通过查看其源码,在View的draw里面,它通常需要做以下几件事情:

1,绘制自己的背景,如果有的话,因为背景始终都在最后面,所以要先画。

2,如果需要的话,保存canvas的layer来准备绘制渐变效果,比如说有alpha动画等。

3,绘制View的内容,其实就是调用onDraw方法,让子类可以绘制自己的内容。

4,绘制自己的child,具体怎么绘制孩子,ViewGroup会去重写相应的方法。基类的View只是把调用这绘制child的方法,当然这个方法在View里面,应该是什么都不做。

5,如果需要的话,画渐变效果并还原保存的canvas层。

6,绘制其他的元素,比如scrollbar等。


通常,View里面要做这些事情,而且顺序不能改变。

注意第4点,绘制子view的方法,这样的话,如果在绘制子view之前,要是给canvas设置圆角的path,是不是就可以呢。

/**
* 圆角的RelativeLayout
*
*/

public class RoundRectLayout extends RelativeLayout {

private Path mPath;
private int mRadius;

private int mWidth;
private int mHeight;
private int mLastRadius;

public static final int MODE_NONE = 0;
public static final int MODE_ALL = 1;
public static final int MODE_LEFT = 2;
public static final int MODE_TOP = 3;
public static final int MODE_RIGHT = 4;
public static final int MODE_BOTTOM = 5;

private int mRoundMode = MODE_ALL;

public RoundRectLayout(Context context) {
super(context);

init();
}

public RoundRectLayout(Context context, AttributeSet attrs) {
super(context, attrs);

init();
}

private void init(){

setBackgroundDrawable(new ColorDrawable(0x33ff0000));

mPath = new Path();
mPath.setFillType(Path.FillType.EVEN_ODD);

setCornerRadius(ViewUtil.dp2px(getContext(), 30));
}

/**
* 设置是否圆角裁边
* @param roundMode
*/
public void setRoundMode(int roundMode){
mRoundMode = roundMode;
}

/**
* 设置圆角半径
* @param radius
*/
public void setCornerRadius(int radius){
mRadius = radius;
}

private void checkPathChanged(){

if(getWidth() == mWidth && getHeight() == mHeight && mLastRadius == mRadius){
return;
}

mWidth = getWidth();
mHeight = getHeight();
mLastRadius = mRadius;

mPath.reset();

switch (mRoundMode){
case MODE_ALL:
mPath.addRoundRect(new RectF(0, 0, mWidth, mHeight), mRadius, mRadius, Path.Direction.CW);
break;
case MODE_LEFT:
mPath.addRoundRect(new RectF(0, 0, mWidth, mHeight),
new float[]{mRadius, mRadius, 0, 0, 0, 0, mRadius, mRadius},
Path.Direction.CW);
break;
case MODE_TOP:
mPath.addRoundRect(new RectF(0, 0, mWidth, mHeight),
new float[]{mRadius, mRadius, mRadius, mRadius, 0, 0, 0, 0},
Path.Direction.CW);
break;
case MODE_RIGHT:
mPath.addRoundRect(new RectF(0, 0, mWidth, mHeight),
new float[]{0, 0, mRadius, mRadius, mRadius, mRadius, 0, 0},
Path.Direction.CW);
break;
case MODE_BOTTOM:
mPath.addRoundRect(new RectF(0, 0, mWidth, mHeight),
new float[]{0, 0, 0, 0, mRadius, mRadius, mRadius, mRadius},
Path.Direction.CW);
break;
}

}

@Override
public void draw(Canvas canvas) {

if(mRoundMode != MODE_NONE){
int saveCount = canvas.save();

checkPathChanged();

canvas.clipPath(mPath);
super.draw(canvas);

canvas.restoreToCount(saveCount);
}else {
super.draw(canvas);
}

}
}


这里,复写draw方法,在super.draw方法之前,给当前canvas设置path,这个path设置为圆角的方形。

然后在这个view包裹在自 view外,

<round.llh.com.roundtotalview.RoundRectLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_margin="20dp">

<android.support.v7.widget.RecyclerView
android:id="@+id/rv_list_4"
android:layout_width="150dp"
android:layout_height="150dp" />
</round.llh.com.roundtotalview.RoundRectLayout>


这样经测试,5.0以上和以下版本效果都是可以的,当然消耗也是存在的,每次绘制之前,都要canvas.clipPath(mPath);,在复杂的view或者动画中,有可能不流畅,所有请谨慎使用。

以上四种方法各有利弊,大家可以选择自己合适的方式来实现圆角。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: