标签:
好长时间没有写相关的博客了,前几周在帮学姐做毕设,所以博客方面有些耽误。过程中写了一个类似wp的磁贴的view,想再写个配套的layout,所以昨天看了一下自定义viewGroup的相关知识…晚上睡觉想了一下可行性不是很高…代码量还不如直接自己在xml上写来得快,速度上也是个问题。今天看了一下张鸿洋老师的 Android 自定义View (三) 圆环交替 等待效果这篇博文,再加上前一段时间看到的一幅图,结合之前写的一个圆形imageView的实现博文Android自定义View学习笔记03,于是有了将二者相结合一下的念头,下午和晚上动手实践了一下,效果还不错。
给我灵感的图片:
由图看出,只需要在原来圆形imageView的基础上,将图片外的圆环改为有一定宽度和弧度的圆弧,在圆弧下加上两行文本,就可以实现上图的效果。
但是光是这样,和上一篇博客就没有什么大的区别了。所以,参考 Android 自定义View (三) 圆环交替 等待效果这篇博客,做了一个简单的动画效果:手指按下,外边框变长,直到将内部图片完全包裹为止,手指移开,外框变短,恢复原状。
另外,过多的使用类似的view,即使将原图做了缩放处理,仍然有可能出现OOM的情况,所以在使用图片时,可以使用软引用来缓存图片。有关缓存图片的代码见一次OOM引起的优化。
先放上相关代码再分析
自定义view代码如下:
package mmrx.com.myuserdefinedview.textview;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.widget.ImageView;
import mmrx.com.myuserdefinedview.R;
/**
* Created by mmrx on 2015/6/5.
*/
public class CustomRIV2 extends ImageView {
private Context mContext;
private Bitmap mBitmap;
//图片画笔和边框画笔和文字画笔
private Paint mBitMapPaint;
private Paint mBorderPaint;
private Paint mTextPaint;
//边框颜色
private int mBorderColor = Color.WHITE;
//边框宽度 边框宽度为原图半径的1/8
private float mBorderWidth = 5f;
//边框比圆形图片半径大本身的1/2,也就是说边框和图片的距离为 边框宽度的1/2
private int BORDER = 10;
//第一行和第二行文字之间的距离为2
private final float TEXT = 2f;
//边框和文字之间的距离 等于边框的宽度*2
private float BORDER_TEXT = 10f;
//边框角度
private float mBorderAngle = 90f;
private float mBorderAngle_ = 90f;//最初设定的数值
//文字大小
private int mTextSize = 18;
//第一行文//
private String mTextRow1 = "";
//第二行
private String mTextRow2 = "";
//第一行文字颜色
private int mTextColorRow1 = Color.BLACK;
//第二行文字颜色
private int mTextColorRow2 = Color.GRAY;
//渲染器
private BitmapShader mBitMapShader;
//图片拉伸方式 默认为按比例缩放
private int mImageScale = 1;
//控件的长宽
private int mWidth;
private int mHeight;
//边框和圆形图片的半径
private float mBorderRadius;
private float mDrawableRadius;
//矩形
private RectF mDrawableRect;
private RectF mBorderRect;
private RectF mOuterBorderRect;
private Rect mRow1Rect;
private Rect mRow2Rect;
//变换矩形
private Matrix mBitMapMatrix;
//控制圆环变化的标识符
boolean isAdd = true;
boolean isDecreased = true;
//圆环增减的速度
final int mSpeed = 5;
public CustomRIV2(Context mContext){
super(mContext);
this.mContext = mContext;
}
public CustomRIV2(Context mContext,AttributeSet attr){
this(mContext, attr, R.attr.CustomImageView04Style);
this.mContext = mContext;
}
public CustomRIV2(Context mContext,AttributeSet attr,int defSytle){
super(mContext,attr,defSytle);
TypedArray ta = mContext.obtainStyledAttributes(attr,R.styleable.CustomRIV2_style,
defSytle,R.style.CustomizeStyle04);
int count = ta.getIndexCount();
for(int i=0;i<count;i++){
int index = ta.getIndex(i);
switch (index){
case R.styleable.CustomRIV2_style_borderColor:
mBorderColor = ta.getColor(index,Color.WHITE);
break;
case R.styleable.CustomRIV2_style_image:
mBitmap = BitmapFactory.decodeResource(getResources(),ta.getResourceId(index,0));
break;
case R.styleable.CustomRIV2_style_imageScaleType:
mImageScale = ta.getInt(index,0);
break;
case R.styleable.CustomRIV2_style_titleSize:
mTextSize = ta.getDimensionPixelSize(index,
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
18, getResources().getDisplayMetrics()));
break;
case R.styleable.CustomRIV2_style_borderAngle:
mBorderAngle = ta.getFloat(index,90);
mBorderAngle_ = mBorderAngle;
break;
case R.styleable.CustomRIV2_style_titleText:
mTextRow1 = ta.getString(index);
break;
case R.styleable.CustomRIV2_style_contentText:
mTextRow2 = ta.getString(index);
break;
case R.styleable.CustomRIV2_style_titleColor:
mTextColorRow1 = ta.getColor(index,Color.BLACK);
break;
case R.styleable.CustomRIV2_style_contentColor:
mTextColorRow2 = ta.getColor(index,Color.GRAY);
break;
default:
break;
}
}
ta.recycle();
mBorderPaint = new Paint();
mBitMapPaint = new Paint();
mTextPaint = new Paint();
mDrawableRect = new RectF();
mOuterBorderRect = new RectF();
mBorderRect = new RectF();
mRow1Rect = new Rect();
mRow2Rect = new Rect();
//计算字体所需范围
mTextPaint.setTextSize(mTextSize);
mTextPaint.getTextBounds(mTextRow1,0,mTextRow1.length(),mRow1Rect);
mTextPaint.setTextSize(mTextSize*4/5);
mTextPaint.getTextBounds(mTextRow2,0,mTextRow2.length(),mRow2Rect);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int heightMod = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int widthMod = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int row1width = 0,row2width = 0;
int row1height = 0,row2height = 0;
//march_parent & exactly dimen
if(heightMod == MeasureSpec.EXACTLY){
mHeight = heightSize;
}
//wrap_content & others
else{
//获得图片高度
mHeight = mBitmap.getHeight();
}
if(widthMod == MeasureSpec.EXACTLY){
mWidth = widthSize;
}
//wrap_content & others
else{
//获得图片宽度
mWidth = mBitmap.getWidth();
}
//获得文字的尺寸
row1width = mRow1Rect.width();
row2width = mRow2Rect.width();
row1height = mRow1Rect.height();
row2height = mRow2Rect.height();
//图片的半径
mDrawableRadius = Math.min(mWidth,mHeight);
mBorderWidth = (int)(mDrawableRadius/10);
BORDER = (int)(mBorderWidth/3);
BORDER_TEXT = mBorderWidth*2;
//考虑线条的宽度,如果没有考虑线条宽度,显示会把线条的一部分遮蔽
//外边框的半径
mBorderRadius = mDrawableRadius + BORDER + mBorderWidth;
//计算控件的高度
int height =(int)((mBorderRadius+mBorderWidth)*2 + BORDER_TEXT + row1height + TEXT + row2height + 0.5);
//计算控件的宽度
int textWidth = Math.max(row1width,row2width);
int width = Math.max(textWidth,(int)(mBorderRadius*2+0.5))+(int)mBorderWidth;
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
if(mBitmap == null)
return;
//设置渲染器
mBitMapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
//抗锯齿
mBitMapPaint.setAntiAlias(true);
//设置渲染器
mBitMapPaint.setShader(mBitMapShader);
//设置外框的相关参数
mBorderPaint.setStyle(Paint.Style.STROKE);
mBorderPaint.setAntiAlias(true);
mBorderPaint.setColor(mBorderColor);
mBorderPaint.setStrokeWidth(mBorderWidth);
//边框矩形
mBorderRect.set(0,0,getWidth(),getHeight());
//图片外框的矩形,因为图片是在外边框内部,所以位置矩形的坐标要考虑到边框的宽度
final float borderLeft = getWidth()/2-mBorderRadius;
final float borderTop = (getHeight()-mRow1Rect.height()-mRow2Rect.height()-BORDER_TEXT-TEXT)/2-mBorderRadius+mBorderWidth;
final float borderRight = getWidth()/2+mBorderRadius;
final float borderBottom = getHeight()-mRow1Rect.height()-mRow2Rect.height()-BORDER_TEXT-TEXT;
mOuterBorderRect.set(borderLeft,borderTop,
borderRight,borderBottom);
//图片的矩形
mDrawableRect.set(borderLeft-BORDER,
borderTop+BORDER,
borderRight-BORDER,
borderBottom-BORDER);
//设置图片的缩放
setBitMapScale();
canvas.drawCircle(getWidth()/2,
(getHeight()-mRow1Rect.height()-mRow2Rect.height()-BORDER_TEXT-TEXT)/2+mBorderWidth,
mDrawableRadius,mBitMapPaint);
if(mBorderWidth != 0) {
//设置起始角度 0度位置为三点钟方向
float mBorderAngle_start = 135 - mBorderAngle/2;
canvas.drawArc(mOuterBorderRect, mBorderAngle_start, mBorderAngle, false, mBorderPaint);
}
//绘制文字
if(mTextRow1 != null){
//设置字体画笔相关参数
mTextPaint.setStyle(Paint.Style.FILL);
mTextPaint.setColor(mTextColorRow1);
mTextPaint.setTextSize(mTextSize);
mTextPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(mTextRow1,getWidth()/2,borderBottom+BORDER_TEXT+mRow1Rect.height()/2,mTextPaint);
}
if(mTextRow2 != null){
//设置字体画笔相关参数
mTextPaint.setStyle(Paint.Style.FILL);
mTextPaint.setColor(mTextColorRow2);
mTextPaint.setTextSize(mTextSize*4/5);
mTextPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(mTextRow2,getWidth()/2,borderBottom+BORDER_TEXT+mRow1Rect.height()+TEXT+mRow2Rect.height()/2,mTextPaint);
}
}
//根据控件的尺寸和设置的图片缩放模式,来对图片进行缩放
private void setBitMapScale(){
float scaleX = 0,scaleY = 0;
//获得圆形的直径和图片的尺寸
float diameter = mDrawableRadius*2;
float mBitMapWidth = mBitmap.getWidth();
float mBitMapHeight = mBitmap.getHeight();
mBitMapMatrix = new Matrix();
mBitMapMatrix.set(null);
//fillXY 宽高单独缩放
if(mImageScale == 0){
scaleX = diameter/mBitMapWidth;
scaleY = diameter/mBitMapHeight;
}
//center 等比例缩放
else{
float scale = 0;
scaleX = diameter/mBitMapWidth;
scaleY = diameter/mBitMapHeight;
//如果宽度和高度至少有一个需要放大
if(scaleX > 1 || scaleY > 1){
scale = Math.max(scaleX,scaleY);
}
else{
scale = Math.min(scaleX, scaleY);
}
scaleX = scale;
scaleY = scale;
}
mBitMapMatrix.setScale(scaleX,scaleY);
mBitMapShader.setLocalMatrix(mBitMapMatrix);
}
//设置图片
public void setImage(Bitmap bm){
this.mBitmap = bm;
invalidate();
}
//设置图片
public void setImage(int rid){
this.mBitmap = BitmapFactory.decodeResource(getResources(),rid);
invalidate();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.v("CustomRIV2-onTouchEvent","ACTION_DOWN");
isDecreased = false;
isAdd = true;
new Thread(new AddRunnable()).start();
break;
case MotionEvent.ACTION_UP:
Log.v("CustomRIV2-onTouchEvent","ACTION_UP");
isDecreased = true;
isAdd = false;
new Thread(new DecreaseRunnable()).start();
break;
default:
break;
}
return true;
}
private class AddRunnable implements Runnable{
@Override
public void run() {
while (isAdd && mBorderAngle <= 360) {
mBorderAngle += 2;
postInvalidate();
try {
Thread.sleep(mSpeed);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
}
};
private class DecreaseRunnable implements Runnable{
@Override
public void run() {
while (isDecreased && mBorderAngle >mBorderAngle_) {
mBorderAngle -= 2;
postInvalidate();
try {
Thread.sleep(mSpeed);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
}
};
}
xml文件中相关代码
<!--attrs.xml-->
<attr name="titleText" format="string"/>
<attr name="titleColor" format="color"/>
<attr name="contentColor" format="color"/>
<attr name="contentText" format="string"/>
<attr name="titleSize" format="dimension"/>
<attr name="image" format="reference"/>
<attr name="imageScaleType">
<enum name="fillXY" value="0"/>
<enum name="center" value="1"/>
</attr>
<!--边框颜色-->
<attr name="borderColor" format="color"/>
<!--边框角度-->
<attr name="borderAngle" format="float"/>
<attr name="CustomImageView04Style" format="reference"/>
<declare-styleable name="CustomRIV2_style">
<attr name="image"/>
<!--<attr name="borderWidth"/>-->
<attr name="borderColor"/>
<attr name="imageScaleType"/>
<attr name="borderAngle"/>
<attr name="titleText"/>
<attr name="titleSize" />
<attr name="contentText"/>
<attr name="titleColor"/>
<attr name="contentColor"/>
</declare-styleable>
<!--styles.xml-->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="CustomView01Style">@style/CustomizeStyle01</item>
<item name="CustomImageView02Style">@style/CustomizeStyle02</item>
<item name="CustomImageView03Style">@style/CustomizeStyle03</item>
<item name="CustomImageView04Style">@style/CustomizeStyle04</item>
</style>
<style name="CustomizeStyle04">
<item name="imageScaleType">center</item>
<item name="image">@drawable/hello</item>
<item name="borderWidth">5dp</item>
<item name="borderColor">#ff00</item>
<item name="borderAngle">90</item>
</style>
<!--在布局文件中这样,部分属性没有用到-->
<mmrx.com.myuserdefinedview.textview.CustomRIV2
android:layout_width="70dp"
android:layout_height="70dp"
customview:borderWidth="5dp"
customview:borderColor="#ff868686"
customview:image="@drawable/hello"
customview:titleText="詹维斯·卡维泽"
customview:titleSize="18dp"
customview:contentText="《疑犯追踪》男主角"
customview:borderAngle="120"/>
然后显示如图
不会制作动图…大致的效果看完鸿洋老师的那篇博文后脑补吧~~~
相比于上一篇博文Android自定义View学习笔记03,这里增加的东西有:
1. 新增相关的属性(在attrs.xml
中)。
2. 在onMeasure
方法中增加边框尺寸的计算、内圆半径计算、外圆半径计算,外圆框宽度计算。
3. 在onDraw
中增加外圆框圆弧的绘制、文字的绘制。
4. 增加两个实现了Runnable
接口的内部类,用于动态改变圆弧角度。
attrs.xml
文件中给出,半径动态设置为内圆图片半径加外圆框宽度加内外圆之间的距离;而外圆框的宽度为内圆半径的1/10,内外圆之间的距离为外圆框的1/3。mDrawableRadius = Math.min(mWidth,mHeight);//内圆半径
mBorderWidth = (int)(mDrawableRadius/10);
BORDER = (int)(mBorderWidth/3);
mBorderRadius = mDrawableRadius + BORDER + mBorderWidth;
//控制圆环变化的标识符
boolean isAdd = true;
boolean isDecreased = true;
//圆环增减的速度
final int mSpeed = 5;
前连个标识符分别用于控制圆弧弧度增加和圆弧弧度减小线程的while循环,而mSpeed
则是表示速度,在这里为每5ms弧度增加2度。
这两个线程也很简单
private class AddRunnable implements Runnable{
@Override
public void run() {
//isAdd为真且外圆框没有完全包裹内圆时,每5ms圆弧增加2度
while (isAdd && mBorderAngle <= 360) {
mBorderAngle += 2;
postInvalidate();
try {
Thread.sleep(mSpeed);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
}
};
private class DecreaseRunnable implements Runnable{
@Override
public void run() {
//isDecreased为真且外圆框没有完全包裹内圆时,每5ms圆弧减少2度
while (isDecreased && mBorderAngle >mBorderAngle_) {
mBorderAngle -= 2;
postInvalidate();
try {
Thread.sleep(mSpeed);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
}
};
case MotionEvent.ACTION_DOWN:
Log.v("CustomRIV2-onTouchEvent","ACTION_DOWN");
isDecreased = false;
isAdd = true;
new Thread(new AddRunnable()).start();
break;
case MotionEvent.ACTION_UP:
Log.v("CustomRIV2-onTouchEvent","ACTION_UP");
isDecreased = true;
isAdd = false;
new Thread(new DecreaseRunnable()).start();
总的来说还是比较简单的。
Android自定义view学习笔记01
Android自定义view学习笔记02
Android自定义view学习笔记03
最后推荐一部美剧《疑犯追踪》,最近正在追,很棒~图片就是男主角。
标签:
原文地址:http://blog.csdn.net/u012123160/article/details/46382169