欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2024-03-05 17:36:01
android大家都有很多需要用户上传头像的需求,有的是选方形,有的是圆角矩形,有的是圆形。 首先我们要做一个处理图片的自定义控件,把传入的图片,经过用户选择区域,处理成...

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

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

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

圆形区域可调节大小。

这个自定义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的,本人不太会讲解,做着玩玩。

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