├── README.md └── code ├── RadarView.java ├── RoundImageTransform.java ├── WaterRadarView.java └── attrs.xml /README.md: -------------------------------------------------------------------------------- 1 | # raderView 2 | ####直接上图看xml preview 静态效果了: 3 | 4 | ![Paste_Image.png](http://upload-images.jianshu.io/upload_images/1019822-a7e3c3d95b0f0b7b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 5 | 6 | **最近更新了一个 水波纹的搜索效果** 7 | 8 | ![Paste_Image.png](http://upload-images.jianshu.io/upload_images/1019822-689f935e935b4e6a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 9 | 10 | 11 | ###目前已经支持在xml中配置: 12 | 1. 设置默认中间图片 13 | 2. 每个圈圈颜色,圈圈的width 14 | 3. 以及扫描块的颜色。 15 | 16 | ###使用方法非常简单: 17 | 如果你想设置中间的图片 ,可以在activity中这么做: 18 | 19 | ```java 20 | public class RecommentFriendsActivity extends BaseActivity { 21 | 22 | private RadarView mRadarView; 23 | @Override 24 | protected void onCreate(@Nullable Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_recomend_paoyou); 27 | mRadarView = (RadarView) findViewById(R.id.radar_view); 28 | } 29 | 30 | @Override 31 | protected void onResume() { 32 | super.onResume(); 33 | mRadarView.start(); 34 | mRadarView.setmImageUrl("http://p5.qhimg.com/t01ba4f7909f15de5fc.jpg"); 35 | } 36 | } 37 | ``` 38 | 39 | ~~会启动一个线程去下载图片。下载好之后替换默认的图。~~ 40 | 41 | 现在更新成使用glide加载图片,生成圆形图片,不在自己使用多线程下载图片,处理图片。代码如下: 42 | 43 | ```java 44 | private void getBitmapFromGlide(String url){ 45 | ImageView imageView = new ImageView(getContext()); 46 | imageView.setLayoutParams(new ViewGroup.MarginLayoutParams((int)mBitmapWidth,(int)mBitmapWidth)); 47 | Glide.with(getContext()).load(url).asBitmap().centerCrop().transform(new RoundImageTransform(getContext())).into(new BitmapImageViewTarget(imageView) { 48 | @Override 49 | protected void setResource(Bitmap resource) { 50 | mBitmap = resource; 51 | } 52 | }); 53 | } 54 | ``` 55 | 唯一的不足时这里new了一个没用的ImageView对象,如果你有更加好的方法,请告诉我。 56 | 57 | 58 | -------------------------------------------------------------------------------- /code/RadarView.java: -------------------------------------------------------------------------------- 1 | package com.brzhang.widget; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.content.res.TypedArray; 6 | import android.graphics.Bitmap; 7 | import android.graphics.BitmapFactory; 8 | import android.graphics.BitmapShader; 9 | import android.graphics.Canvas; 10 | import android.graphics.Color; 11 | import android.graphics.Matrix; 12 | import android.graphics.Paint; 13 | import android.graphics.Shader; 14 | import android.graphics.SweepGradient; 15 | import android.graphics.drawable.BitmapDrawable; 16 | import android.graphics.drawable.Drawable; 17 | import android.support.v4.graphics.drawable.RoundedBitmapDrawable; 18 | import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory; 19 | import android.text.TextUtils; 20 | import android.util.AttributeSet; 21 | import android.view.View; 22 | import android.view.ViewGroup; 23 | import android.widget.ImageView; 24 | 25 | import com.bumptech.glide.Glide; 26 | import com.bumptech.glide.request.target.BitmapImageViewTarget; 27 | import com.tencent.campusx.R; 28 | import java.io.IOException; 29 | import java.io.InputStream; 30 | import java.net.HttpURLConnection; 31 | import java.net.URL; 32 | 33 | 34 | /** 35 | * Created by brzhang on 16/6/23. 36 | * Description : 雷达搜索好友视图 37 | */ 38 | public class RadarView extends View { 39 | 40 | 41 | private String mImageUrl; 42 | private boolean threadIsRunning = false; 43 | private int start = 0; 44 | private RadarThread radarThread; 45 | 46 | private Paint mPaintBitmap;//换中间图片的画笔 47 | private Paint mPaintLine;//话圆圈的画笔 48 | private Paint mPaintCircle; //画雷达的画笔 49 | private Matrix matrix; //重点在这了,通过矩阵变换,做出扫描效果。 50 | 51 | private Bitmap mBitmap; //用户自定义图片的圆角图 52 | private float mBitmapWidth = 150;//中心图片默认的宽度,这里是px 53 | private float mCircleMargin = 30;//距离宽度,默认是px 54 | private float mCircleWidth = 2;//圆圈的画笔宽度,默认px 55 | private int mCircleColor = Color.RED; //最内层圈圈的颜色,下面依次 56 | private int mCircleColorx = Color.RED; 57 | private int mCircleColorxx = Color.RED; 58 | private int mCircleColorxxx = Color.RED; 59 | private int mScanColor = Color.RED; 60 | 61 | private int mdefaultImage;//默认设置的中心图片 62 | private Bitmap mDefaultBitmap;//默认生成的中心图片的圆角图 63 | 64 | 65 | float mWidth;//自定义控件的宽度px 66 | float mHeight;//自定义控件的高度px 67 | 68 | 69 | public RadarView(Context context) { 70 | this(context, null); 71 | } 72 | 73 | public RadarView(Context context, AttributeSet attrs) { 74 | this(context, attrs, 0); 75 | } 76 | 77 | public RadarView(Context context, AttributeSet attrs, int defStyleAttr) { 78 | super(context, attrs, defStyleAttr); 79 | TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.RadarView, defStyleAttr, 0); 80 | 81 | //注意这里拿到的dimen单位都会自动转换,比如你天的dp,实际上会转化为设备对应的px。 82 | mBitmapWidth = attributes.getDimension(R.styleable.RadarView_image_width, mBitmapWidth); 83 | mCircleMargin = attributes.getDimension(R.styleable.RadarView_circle_margin, mCircleMargin); 84 | mCircleWidth = attributes.getDimension(R.styleable.RadarView_circle_width, mCircleWidth); 85 | mCircleColor = attributes.getColor(R.styleable.RadarView_circle_color, mCircleColor); 86 | mCircleColorx = attributes.getColor(R.styleable.RadarView_circle_colorx, mCircleColor); 87 | mCircleColorxx = attributes.getColor(R.styleable.RadarView_circle_colorxx, mCircleColor); 88 | mCircleColorxxx = attributes.getColor(R.styleable.RadarView_circle_colorxxx, mCircleColor); 89 | mScanColor = attributes.getColor(R.styleable.RadarView_saner_color, mScanColor); 90 | mdefaultImage = attributes.getResourceId(R.styleable.RadarView_default_image,0); 91 | attributes.recycle(); 92 | initView(); 93 | } 94 | 95 | 96 | private void initView() { 97 | mPaintBitmap = new Paint(); 98 | mPaintLine = new Paint(); 99 | mPaintLine.setColor(mCircleColor); 100 | mPaintLine.setStrokeWidth(mCircleWidth); 101 | mPaintLine.setAntiAlias(true); 102 | mPaintLine.setStyle(Paint.Style.STROKE); 103 | mPaintCircle = new Paint(); 104 | mPaintCircle.setColor(mScanColor); 105 | //mPaintCircle.setStyle(Paint.Style.STROKE); 106 | mPaintCircle.setAntiAlias(true); 107 | matrix = new Matrix(); 108 | //start(); 109 | if (mdefaultImage != 0) {//生成默认图片 110 | getBitmapFromGlide(mdefaultImage); 111 | } 112 | } 113 | 114 | public void setmImageUrl(String mImageUrl) { 115 | this.mImageUrl = mImageUrl; 116 | getBitmapFromGlide(this.mImageUrl); 117 | } 118 | 119 | @Override 120 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 121 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 122 | mHeight = getMeasuredHeight(); 123 | mWidth = getMeasuredWidth(); 124 | } 125 | 126 | @Override 127 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 128 | super.onLayout(changed, left, top, right, bottom); 129 | } 130 | 131 | @Override 132 | protected void onDraw(Canvas canvas) { 133 | super.onDraw(canvas); 134 | canvas.translate(mWidth / 2 - mBitmapWidth / 2, mHeight / 2 - mBitmapWidth / 2); 135 | if (mBitmap != null) { 136 | mPaintBitmap.reset(); 137 | /* // 通过Bitmap和指定x,y方向的平铺方式构造出BitmapShader对象 138 | BitmapShader mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, 139 | Shader.TileMode.CLAMP); 140 | // 将BitmapShader设置到当前的Paint对象中 141 | mPaintBitmap.setShader(mBitmapShader); 142 | 143 | canvas.drawCircle(mBitmapWidth / 2, mBitmapWidth / 2, mBitmapWidth / 2, mPaintBitmap);*/ 144 | 145 | canvas.drawBitmap(mBitmap, 0, 0, mPaintBitmap); 146 | 147 | }else if(mDefaultBitmap != null){ 148 | mPaintBitmap.reset(); 149 | // 通过Bitmap和指定x,y方向的平铺方式构造出BitmapShader对象 150 | BitmapShader mBitmapShader = new BitmapShader(mDefaultBitmap, Shader.TileMode.CLAMP, 151 | Shader.TileMode.CLAMP); 152 | // 将BitmapShader设置到当前的Paint对象中 153 | mPaintBitmap.setShader(mBitmapShader); 154 | 155 | canvas.drawCircle(mBitmapWidth / 2, mBitmapWidth / 2, mBitmapWidth / 2, mPaintBitmap); 156 | } 157 | canvas.translate(mBitmapWidth / 2, mBitmapWidth / 2); 158 | // 画4个圆圈 159 | mPaintLine.setColor(mCircleColor); 160 | canvas.drawCircle(0, 0, mBitmapWidth / 2 + mCircleMargin, mPaintLine); 161 | mPaintLine.setColor(mCircleColorx); 162 | canvas.drawCircle(0, 0, mBitmapWidth / 2 + mCircleMargin * 2, mPaintLine); 163 | mPaintLine.setColor(mCircleColorxx); 164 | canvas.drawCircle(0, 0, mBitmapWidth / 2 + mCircleMargin * 3, mPaintLine); 165 | mPaintLine.setColor(mCircleColorxxx); 166 | canvas.drawCircle(0, 0, mBitmapWidth / 2 + mCircleMargin * 4, mPaintLine); 167 | 168 | // 绘制渐变圆 169 | 170 | Shader shader = new SweepGradient(0, 0, Color.TRANSPARENT, mScanColor); 171 | 172 | mPaintCircle.setShader(shader); 173 | 174 | //增加旋转动画,用矩阵帮忙 175 | 176 | canvas.concat(matrix); 177 | 178 | canvas.drawCircle(0, 0, mBitmapWidth / 2 + mCircleMargin * 4 + 100, mPaintCircle); 179 | 180 | } 181 | 182 | 183 | public void start() { 184 | if (threadIsRunning) { 185 | return; 186 | } 187 | threadIsRunning = true; 188 | radarThread = new RadarThread(); 189 | radarThread.start(); 190 | } 191 | 192 | public void stop() { 193 | threadIsRunning = false; 194 | } 195 | 196 | 197 | private void getBitmapFromGlide(int resource){ 198 | ImageView imageView = new ImageView(getContext()); 199 | imageView.setLayoutParams(new ViewGroup.MarginLayoutParams((int)mBitmapWidth,(int)mBitmapWidth)); 200 | Glide.with(getContext()).load(resource).asBitmap().centerCrop().transform(new RoundImageTransform(getContext())).into(new BitmapImageViewTarget(imageView) { 201 | @Override 202 | protected void setResource(Bitmap resource) { 203 | mDefaultBitmap = resource; 204 | } 205 | }); 206 | } 207 | private void getBitmapFromGlide(String url){ 208 | ImageView imageView = new ImageView(getContext()); 209 | imageView.setLayoutParams(new ViewGroup.MarginLayoutParams((int)mBitmapWidth,(int)mBitmapWidth)); 210 | Glide.with(getContext()).load(url).asBitmap().centerCrop().transform(new RoundImageTransform(getContext())).into(new BitmapImageViewTarget(imageView) { 211 | @Override 212 | protected void setResource(Bitmap resource) { 213 | mBitmap = resource; 214 | } 215 | }); 216 | } 217 | 218 | class RadarThread extends Thread { 219 | 220 | @Override 221 | public void run() { 222 | while (threadIsRunning) { 223 | RadarView.this.post(new Runnable() { 224 | @Override 225 | public void run() { 226 | start = start + 1; 227 | matrix.setRotate(start, 0, 0); //因为我对画笔进行了平移,0,0表示绕圆的中心点转动 228 | RadarView.this.invalidate(); 229 | } 230 | }); 231 | try { 232 | Thread.sleep(5); 233 | } catch ( InterruptedException e ) { 234 | e.printStackTrace(); 235 | } 236 | } 237 | } 238 | } 239 | 240 | 241 | } -------------------------------------------------------------------------------- /code/RoundImageTransform.java: -------------------------------------------------------------------------------- 1 | package com.brzhang.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapShader; 6 | import android.graphics.Canvas; 7 | import android.graphics.Paint; 8 | 9 | import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; 10 | import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; 11 | 12 | /** 13 | * Created by brzhang on 16/6/25. 14 | * Description : 15 | */ 16 | public class RoundImageTransform extends BitmapTransformation { 17 | 18 | public RoundImageTransform(Context context) { 19 | super(context); 20 | } 21 | 22 | //其中outWidth 和 outHeight是你传入的图片的宽高 23 | @Override 24 | protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { 25 | return circleCrop(pool, toTransform,outWidth,outHeight); 26 | } 27 | 28 | private static Bitmap circleCrop(BitmapPool pool, Bitmap source,int outwidth,int outHeight) { 29 | if (source == null) return null; 30 | 31 | int size = Math.min(source.getWidth(), source.getHeight()); 32 | int x = (source.getWidth() - size) / 2; 33 | int y = (source.getHeight() - size) / 2; 34 | 35 | Bitmap squared = Bitmap.createBitmap(source, x, y, outwidth, outHeight); 36 | 37 | Bitmap result = pool.get(outwidth, outHeight, Bitmap.Config.ARGB_8888); 38 | if (result == null) { 39 | result = Bitmap.createBitmap(outwidth, outHeight, Bitmap.Config.ARGB_8888); 40 | } 41 | 42 | Canvas canvas = new Canvas(result); 43 | Paint paint = new Paint(); 44 | paint.setShader(new BitmapShader(squared, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP)); 45 | paint.setAntiAlias(true); 46 | float r = outwidth / 2f; 47 | canvas.drawCircle(r, r, r, paint); 48 | return result; 49 | } 50 | 51 | @Override 52 | public String getId() { 53 | return getClass().getName(); 54 | } 55 | } -------------------------------------------------------------------------------- /code/WaterRadarView.java: -------------------------------------------------------------------------------- 1 | package com.tencent.campusx.comm.widget; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Bitmap; 6 | import android.graphics.Canvas; 7 | import android.graphics.Color; 8 | import android.graphics.Matrix; 9 | import android.graphics.Paint; 10 | import android.util.AttributeSet; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.view.animation.DecelerateInterpolator; 14 | import android.view.animation.LinearInterpolator; 15 | import android.widget.ImageView; 16 | 17 | import com.bumptech.glide.Glide; 18 | import com.bumptech.glide.request.target.BitmapImageViewTarget; 19 | import com.tencent.campusx.R; 20 | import com.tencent.campusx.comm.utils.RoundImageTransform; 21 | import com.tencent.campusx.comm.utils.SystemUtils; 22 | 23 | 24 | /** 25 | * Created by brzhang on 16/6/23. 26 | * Description : 水波纹雷达搜索好友视图 27 | */ 28 | public class WaterRadarView extends View { 29 | 30 | 31 | private String mImageUrl; 32 | private boolean threadIsRunning = false; 33 | private int start = 0; 34 | private RadarThread radarThread; 35 | 36 | private Paint mPaintBitmap;//换中间图片的画笔 37 | private Paint mPaintCircle; //画雷达的画笔 38 | private Matrix matrix; //重点在这了,通过矩阵变换,做出扫描效果。 39 | 40 | private LinearInterpolator mLinearInterpolator; 41 | private DecelerateInterpolator mDecelerateInterpolator; 42 | 43 | private Bitmap mBitmap; //用户自定义图片的圆角图 44 | private float mBitmapWidth = 150;//中心图片默认的宽度,这里是px 45 | private float mCircleWidth = 2;//圆圈的画笔宽度,默认px 46 | private int mCircleColor = Color.RED; //最内层圈圈的颜色,下面依次 47 | 48 | private int mCircleCount = 6; 49 | 50 | private int mdefaultImage;//默认设置的中心图片 51 | private Bitmap mDefaultBitmap;//默认生成的中心图片的圆角图 52 | 53 | 54 | float mWidth;//自定义控件的宽度px 55 | float mHeight;//自定义控件的高度px 56 | 57 | private int screenWidth; 58 | 59 | 60 | public WaterRadarView(Context context) { 61 | this(context, null); 62 | } 63 | 64 | public WaterRadarView(Context context, AttributeSet attrs) { 65 | this(context, attrs, 0); 66 | } 67 | 68 | public WaterRadarView(Context context, AttributeSet attrs, int defStyleAttr) { 69 | super(context, attrs, defStyleAttr); 70 | TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.RadarView, defStyleAttr, 0); 71 | //注意这里拿到的dimen单位都会自动转换,比如你天的dp,实际上会转化为设备对应的px。 72 | mBitmapWidth = attributes.getDimension(R.styleable.RadarView_image_width, mBitmapWidth); 73 | mCircleWidth = attributes.getDimension(R.styleable.RadarView_circle_width, mCircleWidth); 74 | mCircleColor = attributes.getColor(R.styleable.RadarView_circle_color, mCircleColor); 75 | mdefaultImage = attributes.getResourceId(R.styleable.RadarView_default_image, 0); 76 | attributes.recycle(); 77 | mLinearInterpolator = new LinearInterpolator(); 78 | mDecelerateInterpolator = new DecelerateInterpolator(); 79 | screenWidth = SystemUtils.getScreenWidth(context); 80 | initView(); 81 | } 82 | 83 | 84 | private void initView() { 85 | mPaintBitmap = new Paint(); 86 | mPaintCircle = new Paint(); 87 | mPaintCircle.setColor(mCircleColor); 88 | mPaintCircle.setAntiAlias(true); 89 | matrix = new Matrix(); 90 | if (mdefaultImage != 0) {//生成默认图片 91 | getBitmapFromGlide(mdefaultImage); 92 | } 93 | } 94 | 95 | public void setmImageUrl(String mImageUrl) { 96 | this.mImageUrl = mImageUrl; 97 | getBitmapFromGlide(this.mImageUrl); 98 | } 99 | 100 | @Override 101 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 102 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 103 | mHeight = getMeasuredHeight(); 104 | mWidth = getMeasuredWidth(); 105 | } 106 | 107 | @Override 108 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 109 | super.onLayout(changed, left, top, right, bottom); 110 | } 111 | 112 | @Override 113 | protected void onDraw(Canvas canvas) { 114 | super.onDraw(canvas); 115 | 116 | canvas.translate(mWidth / 2, mHeight / 2); 117 | // 画4个圆圈 ,一次最最大,次大,到最小 118 | 119 | drawCircleIntellgent(canvas, start % mCircleCount); 120 | 121 | canvas.translate(-mBitmapWidth / 2, -mBitmapWidth / 2); 122 | 123 | if (mBitmap != null) { 124 | mPaintBitmap.reset(); 125 | canvas.drawBitmap(mBitmap, 0, 0, mPaintBitmap); 126 | 127 | } else if (mDefaultBitmap != null) { 128 | mPaintBitmap.reset(); 129 | canvas.drawBitmap(mDefaultBitmap, 0, 0, mPaintBitmap); 130 | } 131 | 132 | } 133 | 134 | private void drawCircleIntellgent(Canvas canvas, int flag) { 135 | 136 | for (int i = 1; i < flag; i++) { 137 | mPaintCircle.setColor(mCircleColor); 138 | float alpha = Math.min(255f, mLinearInterpolator.getInterpolation((mCircleCount - i) * 1f / (mCircleCount - 1)) * 255); 139 | mPaintCircle.setAlpha((int) alpha); 140 | float radius = mDecelerateInterpolator.getInterpolation(i * 1f / (mCircleCount - 1)) * (mBitmapWidth / 2 + screenWidth / 2 - 150); 141 | canvas.drawCircle(0, 0, radius, mPaintCircle); 142 | } 143 | } 144 | 145 | 146 | public void start() { 147 | if (threadIsRunning) { 148 | return; 149 | } 150 | threadIsRunning = true; 151 | radarThread = new RadarThread(); 152 | radarThread.start(); 153 | } 154 | 155 | public void stop() { 156 | threadIsRunning = false; 157 | } 158 | 159 | 160 | private void getBitmapFromGlide(int resource) { 161 | ImageView imageView = new ImageView(getContext()); 162 | imageView.setLayoutParams(new ViewGroup.MarginLayoutParams((int) mBitmapWidth, (int) mBitmapWidth)); 163 | Glide.with(getContext()).load(resource).asBitmap().centerCrop().transform(new RoundImageTransform(getContext())).into(new BitmapImageViewTarget(imageView) { 164 | @Override 165 | protected void setResource(Bitmap resource) { 166 | mDefaultBitmap = resource; 167 | } 168 | }); 169 | } 170 | 171 | private void getBitmapFromGlide(String url) { 172 | ImageView imageView = new ImageView(getContext()); 173 | imageView.setLayoutParams(new ViewGroup.MarginLayoutParams((int) mBitmapWidth, (int) mBitmapWidth)); 174 | Glide.with(getContext()).load(url).asBitmap().centerCrop().transform(new RoundImageTransform(getContext())).into(new BitmapImageViewTarget(imageView) { 175 | @Override 176 | protected void setResource(Bitmap resource) { 177 | mBitmap = resource; 178 | } 179 | }); 180 | } 181 | 182 | class RadarThread extends Thread { 183 | 184 | @Override 185 | public void run() { 186 | while (threadIsRunning) { 187 | WaterRadarView.this.post(new Runnable() { 188 | @Override 189 | public void run() { 190 | start = start + 1; 191 | matrix.setRotate(start, 0, 0); //因为我对画笔进行了平移,0,0表示绕圆的中心点转动 192 | WaterRadarView.this.invalidate(); 193 | } 194 | }); 195 | try { 196 | Thread.sleep(250); 197 | } catch ( InterruptedException e ) { 198 | e.printStackTrace(); 199 | } 200 | } 201 | } 202 | } 203 | 204 | } 205 | -------------------------------------------------------------------------------- /code/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | --------------------------------------------------------------------------------