Android自定义控件仿QQ编辑和选取圆形头像_Android

android大家都有很多需要用户上传头像的需求,有的是选方形,有的是圆角矩形,有的是圆形。
首先我们要做一个处理图片的自定义控件,把传入的图片,经过用户选择区域,处理成一定的形状。

有的app是通过在图片上画一个矩形区域表示选中的内容,有的则是通过双指放大缩小,拖动图片来选取图片。圆形头像,还是改变图片比较好

圆形区域可调节大小。

这个自定义View的图像部分分为三个,背景图片,半透明蒙层,和亮色区域……还是直接贴代码得了

package com.example.jjj.widget;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class RoundEditImageView extends View {
 private Bitmap bitmap;
 RectF clipBounds, dst, src;
 Paint clearPaint;

 // 控件宽高
 private int w, h;
 // 选区半径
 private float radius = 150f;

 // 圆形选区的边界
 private RectF circleBounds;

 // 最大放大倍数
 private float max_scale = 2.0f;

 // 双指的距离
 private float distance;
 private float x0, y0;
 private boolean doublePoint;

 public RoundEditImageView(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
  init();
 }

 public RoundEditImageView(Context context, AttributeSet attrs) {
  super(context, attrs);
  init();
 }

 public RoundEditImageView(Context context) {
  super(context);
  init();
 }

 private void init() {
  clearPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  clearPaint.setColor(Color.GRAY);
  clearPaint.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
 }

 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  if (bitmap != null) {
   canvas.drawBitmap(bitmap, null, dst, null);//每次invalidate通过改变dst达到缩放平移的目的
  }
  // 保存图层,以免清除时清除了bitmap
  int count = canvas.saveLayer(clipBounds, null, Canvas.ALL_SAVE_FLAG);
  canvas.drawColor(0x80000000);
  canvas.drawCircle(w / 2, h / 2, radius, clearPaint);// 清除半透明黑色,留下一个透明圆
  canvas.restoreToCount(count);
 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 }

 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  super.onSizeChanged(w, h, oldw, oldh);
  this.w = w;
  this.h = h;
  // 记录view所占的矩形
  clipBounds = new RectF(0, 0, w, h);
  float l, r, t, b;
  if (bitmap != null) {
   // 判断长宽比,当长宽比太长会太宽是,以fitCenter的方式初始化图片
   if (w / (float) h > bitmap.getWidth() / (float) bitmap.getHeight()) {
    // 图片太高
    float w_ = h * bitmap.getWidth() / (float) bitmap.getHeight();
    l = w / 2f - w_ / 2f;
    r = w / 2f + w_ / 2f;
    t = 0;
    b = h;
   } else {
    // 图片太长,或跟view一样长
    float h_ = w * bitmap.getHeight() / (float) bitmap.getWidth();
    l = 0;
    r = w;
    t = h / 2f - h_ / 2f;
    b = h / 2f + h_ / 2f;
   }
   dst = new RectF(l, t, r, b);// 这个矩形用来变换
   src = new RectF(l, t, r, b);// 这个矩形仅为保存第一次的状态

   max_scale = Math.max(max_scale, bitmap.getWidth() / src.width());
  }
  circleBounds = new RectF(w / 2 - radius, h / 2 - radius, w / 2 + radius, h / 2 + radius);
 }

 public void setImageBitmap(Bitmap bitmap) {
  this.bitmap = bitmap;
  invalidate();
 }

 @Override
 public boolean dispatchTouchEvent(MotionEvent e) {
  if (e.getPointerCount() > 2) {// 不接受多于两指的事件
   return false;
  } else if (e.getPointerCount() == 2) {
   doublePoint = true;// 标志位,记录两指事件处理后,抬起一只手也不处理拖动
   handleDoubleMove(e);
  } else {
   // 处理单指的拖动
   switch (e.getAction()) {
   case MotionEvent.ACTION_DOWN:
    x0 = e.getX();
    y0 = e.getY();
    break;
   case MotionEvent.ACTION_MOVE:
    if (doublePoint) {
     break;
    }
    float x = e.getX();
    float y = e.getY();
    float w = dst.width();
    float h = dst.height();

    // 不允许拖过圆形区域,不能使圆形区域内空白
    dst.left += x - x0;
    if (dst.left > circleBounds.left) {
     dst.left = circleBounds.left;
    } else if (dst.left < circleBounds.right - w) {
     dst.left = circleBounds.right - w;
    }
    dst.right = dst.left + w;

    // 不允许拖过圆形区域,不能使圆形区域内空白
    dst.top += y - y0;
    if (dst.top > circleBounds.top) {
     dst.top = circleBounds.top;
    } else if (dst.top < circleBounds.bottom - h) {
     dst.top = circleBounds.bottom - h;
    }
    dst.bottom = dst.top + h;

    x0 = x;
    y0 = y;
    invalidate();
    break;
   case MotionEvent.ACTION_UP:
    doublePoint = false;// 恢复标志位
    break;
   }
  }

  return true;
 }

 // 处理双指事件
 private void handleDoubleMove(MotionEvent e) {
  switch (e.getAction() & MotionEvent.ACTION_MASK) {
  case MotionEvent.ACTION_POINTER_DOWN:
   distance = sqrt(e);
   Log.d("px", "down:distance=" + distance);
   break;
  case MotionEvent.ACTION_MOVE:
   scale(e);
   break;
  case MotionEvent.ACTION_UP:
   break;
  default:
   break;
  }
 }

 private void scale(MotionEvent e) {
  float dis = sqrt(e);
  // 以双指中心作为图片缩放的支点
  float pX = e.getX(0) / 2f + e.getX(1) / 2f;
  float pY = e.getY(0) / 2f + e.getY(1) / 2f;
  float scale = dis / distance;
  Log.d("px", "move:distance=" + dis + ",scale to" + scale);
  float w = dst.width();
  float h = dst.height();

  if (w * scale < radius * 2 || h * scale < radius * 2 || w * scale > src.width() * max_scale) {
   // 无法缩小到比选区还小,或到达最大倍数
   return;
  }
  // 把dst区域放大scale倍
  dst.left = (dst.left - pX) * scale + pX;
  dst.right = (dst.right - pX) * scale + pX;
  dst.top = (dst.top - pY) * scale + pY;
  dst.bottom = (dst.bottom - pY) * scale + pY;

  // 缩放同样不允许使圆形区域空白
  if (dst.left > circleBounds.left) {
   dst.left = circleBounds.left;
   dst.right = dst.left + w * scale;
  } else if (dst.right < circleBounds.right) {
   dst.right = circleBounds.right;
   dst.left = dst.right - w * scale;
  }

  if (dst.top > circleBounds.top) {
   dst.top = circleBounds.top;
   dst.bottom = dst.top + h * scale;
  } else if (dst.bottom < circleBounds.bottom) {
   dst.bottom = circleBounds.bottom;
   dst.top = dst.bottom - h * scale;
  }
  invalidate();

  distance = dis;
 }

 private float sqrt(MotionEvent e) {
  return (float) Math.sqrt((e.getX(0) - e.getX(1)) * (e.getX(0) - e.getX(1)) + (e.getY(0) - e.getY(1)) * (e.getY(0) - e.getY(1)));
 }

 // 生成目前选区指定大小的圆形bitmap
 public Bitmap extractBitmap(int width) {
  Bitmap outBitmap = Bitmap.createBitmap(width, width, Bitmap.Config.ARGB_8888);
  Canvas canvas = new Canvas(outBitmap);
  Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
  p.setColor(Color.GRAY);
  canvas.drawCircle(width / 2, width / 2, width / 2, p);
  float scale = dst.width() / bitmap.getWidth();
  int w = (int) (circleBounds.width() / scale);
  int l = (int) ((circleBounds.left - dst.left) / scale);
  int r = l + w;
  int t = (int) ((circleBounds.top - dst.top) / scale);
  int b = t + w;
  Rect resRect = new Rect(l, t, r, b);
  Paint paint = new Paint();
  paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
  canvas.drawBitmap(bitmap, resRect, canvas.getClipBounds(), paint);
  return outBitmap;
 }
}

Activity中用法

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_select_header);
  RoundEditImageView imageView = (RoundEditImageView) findViewById(R.id.roundEditImageView1);
  bitmap = BitmapFactory.decodeFile(imagePath);
  imageView.setImageBitmap(bitmap);
 }

 @Override
 public void onClick(View v) {
   //生成一个300*300的当前亮圆形中的图片
   Bitmap result = imageView.extractBitmap(300);
   //压缩成png
   FileOutputStream out = new FileOutputStream(new File(filePath));
   result.compress(Bitmap.CompressFormat.PNG, 100, out);
   //上传与显示
   ...
 }

需求是模仿QQ的,本人不太会讲解,做着玩玩。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索仿QQ选取头像
ios 自定义控件 圆形、ps选取圆形、matlab圆形区域选取、ps怎么选取圆形、ps如何选取圆形,以便于您获取更多的相关知识。

时间: 2024-11-02 13:21:07

Android自定义控件仿QQ编辑和选取圆形头像_Android的相关文章

Android自定义控件仿QQ编辑和选取圆形头像

android大家都有很多需要用户上传头像的需求,有的是选方形,有的是圆角矩形,有的是圆形. 首先我们要做一个处理图片的自定义控件,把传入的图片,经过用户选择区域,处理成一定的形状. 有的app是通过在图片上画一个矩形区域表示选中的内容,有的则是通过双指放大缩小,拖动图片来选取图片.圆形头像,还是改变图片比较好 圆形区域可调节大小. 这个自定义View的图像部分分为三个,背景图片,半透明蒙层,和亮色区域--还是直接贴代码得了 package com.example.jjj.widget; imp

Android实现本地上传图片并设置为圆形头像_Android

先从本地把图片上传到服务器,然后根据URL把头像处理成圆形头像. 因为上传图片用到bmob的平台,所以要到bmob(http://www.bmob.cn)申请密钥. 效果图: 核心代码: 复制代码 代码如下: public class MainActivity extends Activity {         private ImageView iv;         private String appKey="";                //填写你的Applicatio

Android自定义控件仿QQ抽屉效果

其实网上类似的实现已经很多了,原理也并不难,只是网上各种demo运行下来,多少都有一些问题.折腾了半天,决定自己实现一个. 首先我们看看实现效果: 对比网上各类demo,这次要实现的主要表现在以下几点: 1.侧滑显示抽屉view 2.侧滑抽屉隐藏view控件点击事件 3.单击任意item隐藏显示的抽屉view 4.滑动list隐藏显示的抽屉view 5.增加SwipeLayout点击事件和Swipe touch事件判断处理 6.优化快速划开多个抽屉隐藏view时多个SwipeLayout滑动状态

Android 高仿QQ滑动弹出菜单标记已读、未读消息

  在上一篇博客<Android 高仿微信(QQ)滑动弹出编辑.删除菜单效果,增加下拉刷新功能>里,已经带着大家学习如何使用SwipeMenuListView这一开源库实现滑动列表弹出菜单,接下来,将进一步学习,如何为不同的list item呈现不同的菜单,此处我们做一个实例:Android 高仿QQ滑动弹出菜单标记已读.未读消息,看下效果图: 1. 创建项目,并导入SwipeMenuListView类库 2. 创建消息实体bean: public class Msg { public int

Android 高仿QQ图片选择器_Android

当做一款APP,需要选择本地图片时,首先考虑的无疑是系统相册,但是Android手机五花八门,再者手机像素的提升,大图无法返回等异常因数,导致适配机型比较困难,微信.QQ都相继的在自己的APP里集成了图片选择功能,放弃了系统提供的图片选择器,这里仿造QQ做了一个本地图片选择器,PS:之前有人说"仿"写成"防"了,今儿特意注意了下,求不错. 先上一张效果图,无图无真相啊~~~ 实现的效果大概是这样的: 1.单选:跳转到本地图片选择文件夹,选择文件夹后,进入到该文件夹下

Android开发仿QQ空间根据位置弹出PopupWindow显示更多操作效果_Android

我们打开QQ空间的时候有个箭头按钮点击之后弹出PopupWindow会根据位置的变化显示在箭头的上方还是下方,比普通的PopupWindow弹在屏幕中间显示好看的多. 先看QQ空间效果图: 这个要实现这个效果可以分几步进行 1.第一步自定义PopupWindow,实现如图的样式,这个继承PopupWindow自定义布局很容易实现 2.得到点击按钮的位置,根据位置是否在屏幕的中间的上方还是下方,将PopupWindow显示在控件的上方或者下方 3.适配问题,因为PopupWindow上面的操作列表

Android 高仿QQ 沉浸式状态栏_Android

前言: 在进入今天正题前,还是老样子先谈谈感想吧,最近感觉整个都失去了方向感,好迷茫!找工作又失败了,难道Android真的饱和了?这两天我一直没出门,除了下楼哪外卖就是宅宿舍了,静想了许久,我还是不能忘了初心,我相信我找不到工作的原因有很多,最关键的还是要技术够硬才行啊,奔跑吧孩子!接下来我就给大家介绍怎样快速打造沉浸式状态栏吧,虽然感觉有点相见恨晚,但其实不完! 一:何为沉浸式状态栏? 沉浸式状态栏是Google从Android 4.4开始,给我们开发者提供的一套能透明的系统ui样式,这样样

Android 高仿QQ图片选择器

当做一款APP,需要选择本地图片时,首先考虑的无疑是系统相册,但是Android手机五花八门,再者手机像素的提升,大图无法返回等异常因数,导致适配机型比较困难,微信.QQ都相继的在自己的APP里集成了图片选择功能,放弃了系统提供的图片选择器,这里仿造QQ做了一个本地图片选择器,PS:之前有人说"仿"写成"防"了,今儿特意注意了下,求不错. 先上一张效果图,无图无真相啊~~~ 实现的效果大概是这样的: 1.单选:跳转到本地图片选择文件夹,选择文件夹后,进入到该文件夹下

Android 高仿QQ 沉浸式状态栏

前言: 在进入今天正题前,还是老样子先谈谈感想吧,最近感觉整个都失去了方向感,好迷茫!找工作又失败了,难道Android真的饱和了?这两天我一直没出门,除了下楼哪外卖就是宅宿舍了,静想了许久,我还是不能忘了初心,我相信我找不到工作的原因有很多,最关键的还是要技术够硬才行啊,奔跑吧孩子!接下来我就给大家介绍怎样快速打造沉浸式状态栏吧,虽然感觉有点相见恨晚,但其实不完! 一:何为沉浸式状态栏? 沉浸式状态栏是Google从Android 4.4开始,给我们开发者提供的一套能透明的系统ui样式,这样样